From c26ffce04394537e7c235cfa61bd81e3fc79b1e4 Mon Sep 17 00:00:00 2001 From: Vincent Rubinetti Date: Wed, 2 Oct 2024 12:58:45 -0400 Subject: [PATCH] Network visualization (#14) Closes #6 (except for "nice to haves", which we can revisit later) - fix playwright install action - add some nice options to flex component - network viz - settle on cytoscape library - customizable colors/sizes/etc - legend and key - selectable nodes/edges - max nodes slider - expand button - download options - add all available built-in and plugin layout algorithms, except ones with only a few dozen stars on github and ones that crash. explicitly set some key options for each algo to make them look sensible. - add network viz to top of testbed page, with fake generated data - util func to pick key colors - download util funcs --- .../actions/install-playwright/action.yaml | 5 +- frontend/.vscode/extensions.json | 3 +- frontend/bun.lockb | Bin 337565 -> 325779 bytes frontend/package.json | 9 + frontend/src/assets/loading.svg | 8 +- frontend/src/components/Flex.tsx | 9 +- frontend/src/components/Network.module.css | 45 ++ frontend/src/components/Network.tsx | 698 ++++++++++++++++++ frontend/src/components/Slider.module.css | 1 - frontend/src/components/Table.tsx | 2 +- frontend/src/components/Tooltip.tsx | 2 +- frontend/src/global/layout.css | 8 +- frontend/src/global/theme.css | 2 +- frontend/src/pages/Testbed.tsx | 75 +- frontend/src/util/color.ts | 37 + frontend/src/util/download.ts | 46 +- frontend/src/vite-env.d.ts | 7 + 17 files changed, 930 insertions(+), 27 deletions(-) create mode 100644 frontend/src/components/Network.module.css create mode 100644 frontend/src/components/Network.tsx create mode 100644 frontend/src/util/color.ts diff --git a/.github/actions/install-playwright/action.yaml b/.github/actions/install-playwright/action.yaml index 474569d..f3289d4 100644 --- a/.github/actions/install-playwright/action.yaml +++ b/.github/actions/install-playwright/action.yaml @@ -7,6 +7,9 @@ inputs: working-directory: description: Where to install Playwright default: ./ + browsers: + description: Browsers to install + default: chromium webkit firefox outputs: version: @@ -53,7 +56,7 @@ runs: shell: bash if: steps.cache.outputs.cache-hit != 'true' working-directory: ${{ inputs.working-directory }} - run: npx playwright install chromium --with-deps + run: npx playwright install ${{ inputs.browsers }} --with-deps - name: Install just Playwright's dependencies shell: bash diff --git a/frontend/.vscode/extensions.json b/frontend/.vscode/extensions.json index 3db235f..c9c2c63 100755 --- a/frontend/.vscode/extensions.json +++ b/frontend/.vscode/extensions.json @@ -5,6 +5,7 @@ "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "yoavbls.pretty-ts-errors", - "streetsidesoftware.code-spell-checker" + "streetsidesoftware.code-spell-checker", + "AntiAntiSepticeye.vscode-color-picker" ] } diff --git a/frontend/bun.lockb b/frontend/bun.lockb index 3bd933a998c8a64967b0f0e7bbeb45a349d9f31d..5a08991dd0574da8632a189627bc288856bddeab 100755 GIT binary patch delta 85527 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л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

6>0z#>!t{^R z{cbl>=^MK7^Ifw==QqVQ2$OuwO250$e`JQHeWMsyoL6_I;T=s|!Duv$l&oH`rvNAfsYLsDg^?VM2H%oa=!z~G_ zN#M(Rzknd-73P`iioE2v^nAG5w7tgF2l=DA?DgpQF+3wTW#SLd$?6U(Dvh7my<67c zMuy_~Pl~g0Bc(%hlUkG4C}&VH$_Ze*iXq4&LF1PY z^kac9@h|ZW1Q{d^&rUZh4>;wr)NuW~-!5|i13PJK)2y#d;glU#R;Q9`N zxH1Sr*cB2~kig;<1fguoD+qSJhoFE2VXVz-2>O43VDW1RMzDMm$bE#M^BV}l+59&U zq?4eW1fyB|atKC#f?$0)1Y=kk2{b-K;Q1DUNS5*zf@~60lVCjS{SJbdFA(f|2SF73 zP6Cs!5Da<`K{VU-9)dg)H2wg=WES`Vg2ZnSWRPGAlm7^TV+90bK0+{!oghIe33NU| z5XZtlL6H0%f*caWGtJKsxK=_C_Zfm2>~_J7YKI#fS`Z`b6A_N5cIEt zVDVQ7=COPd$W=qo`5OfD+5B%1q?4eW1PfXF3J6BlK(M|7g2k+i1RAvvcz%aqDNFed zK{g4hNwA#tu9WVV6eEB;DzU$keaAjCk%nN<4+xUjt{)KOk)UxE1dIh%L69hbAcF)c zOuiZdMuL?W~Qopr7;(HiLpxmXCnR$su=V0it)Y`2q;iNl*@f=N`$UcIM3O zXEXEn9tiPkIj#8nupI|R}PnQsFGhuCrT$ zKDA{>4(en)TfFjKhQ_2>3U=>8irSu*KXNQ->K12-V7I=C%ik8|;J z|4!+PpS$HX&8gme$0oehnWSdhPSp+$HV(FTu#bFg)BohfiPKI+opI@UGS|iaz|~aE zgBv)-^vExb1vZ4;w6F;Ok(qbLra%7K}%nhLzl{sw0FVM7IN+} zv1{Kwk3REs%D3x1Pw#se)^gRehc!bwObdA&>}IKQdDM}Jw~iIBl{fAfpSpeP=$m6| zk40Tp9RDb*)B8-eSP5M!S{^;t?)83Bxz!Js5UqG+g^W`{Kb9^W<6?U8 zAaqINXCvvsje!q?dne(cio>KDE~jmpwZT1?OC? zd`^_P`i^MWqkF>Jp$2oTG)wkpJKPQ0SvK2-7UrR6NeNJVUQ)4R!)c2HdcU3YS*6y_ zzrnHaXJ^#=dB<4lj+ox7NWQXZ!V!bc`a9RO^NxFEzDs4eaSxeoJDbm3ckX2Jbk}Pg zHjEk|eveoDrs4%j#YeVBUD+}&TzTcf@FNd(lg~GQIWtB3^=<39&u!Ujqo)hPTDnZ^ zDWBhDOpI>AT)i!}J-xb&4$+n!6lmV?e)4;69xZ;-dQnpE(;*9|^_dXYCfht9u0n2n zYQ;WOWR+{!Zbgt-CZn1q|T^0WaPaV5;J=m?;hr>P^d(K2hylAktM|{eJ z^t_R8XYMTNwKsXM+(DE09c6nGR|iB5?j%wyexv!aq~gW?N@EthpYVEv!EEVUAI?3x zo3!7_OZIa1;nl*`GCOt0+rG+QXtUj>@^N4v)Be@VYuju&s@=0=VT$dHquuXWE>z?6 zlEK-MdUww(yrAh6zae&2(~c+34D;C)KXG1OakOXepruo%rEbnI2yU&vTI3nHxY^iCX9ZM>klc={rK zz<5&+CB zZxv+J`S37UZ3Sn*Wh0d-!Y77rm z_S@dD$(HHUT>CX}y7@r+43JKG#M(N6P}WsFkNj@w-<-uZ60n{TsjWO(4+ zqZ7MJ`LGN%0lqbsZrM$-Zj_q#LZLL>o+$`x16UbHLm)-^g_kVk9w8!u0F~+(N;@#V2=6T-5G3* zI@}r16z;skHaCSkO`79$DXR>@J*KP*K^_G0OH`VVeDm@Cr78nc?NO&zyT$cc5}wuf zrqtEuHlL=xyl^{q&*;@AdIpc%TnT-)OVjAwi9)3)*P}08BK`Mx-+$`&M4|!@BrBjD zacI+vygqu1US0E_wpo7WUx7l@b>5Jc1ok&^JYsaOBy$0(T zKgq4$7dqbE#jEYm^t_wNu0;*T_3Qn-kIwU{&o;b1Zn?a%yX#jclMYL&nT00wHdcWj z9#ZF0h4bAL2?TS#x#TADLX*|S1ovIfZP4lTgwZ#KQ$R{>v&>idf3o= zE_31)9p0U&9)Dv>&Hc(IuMRig9pt=BD{;K}oK0n+k198B+mk=aMSw}}>&sb}W^13!pAu&gd17&_X3JQWw3v(8 z14plHx!|TtwAPJoh1U}%+Un=CaxL^k$H1ywovQ3EtIw@z*k{d&9vn8@y&U%aY? zpdhrS#^#ad89j~9A3u-t^SX3?fU}OnhJHx_cFCD}g1oj#?rrE5cm6%woca-5amT~ufyt~K#0X?Kg zbn%OwczE03Gg2cDMRqRf`!T?IQ?{bH=M{&inqfZ-?nszdENNcHB^$zL&Pp0|=d`S| z{Z(Pk6VH;sVyM{`6ubJhuJEY?toi8oQ z^j3IJXP!Ff4;Jd^XC;z~=jNU5<>q&LU;O2kD-SOD7PMg7qtqD(XQ$-rSoxVe(2r3z zyBk04*!${>_bv)f^;!`+@I}&d>vbEG`zox^Vc2B$~`WfWjm`x#p;l6Tz_4u0lQ-FuT8A@p!Oi( zo;5P<@y)InmadXzp=;7v&LI4%PiXdPE9T~-^y&?57qGGcV*jL z{Wp}JG1$JYXz8j(v8T*kZ7XUlzf0{hn`l|vT0-q>$x1hI8f{o|X5Y77Ub`2~>Uq1J z?{?=KA#RD2JgV}qD!&;VWwF{sdB8e<%_}B{&u8_2Ftb_O*oOWl%Q{E+J>Gh@yYCKs zx3DF=>3I$}lglL)f0B6@wpYrlZSmpIu@AhTKh(50asI4d@pkOi3kBDX4e9bBF1q|> z!tMeC|C{r!>(w;Z7~*!VSodLWwZu3v+4#K^_UVw}9Xq`#^$313?qkS4-fA$6TAWaN$wO zzEkjn`D!DmV!J4)X3{zcYFHozwd@cAfs~Lb=nDD@@V&I56bgjwB!xmDQ)!7%M#x4{ zC@W;=C~P2PE%XpJ6tW2v$_d$33gv}NzZF6SA)88}qLAIDu#u3N>LY9{WHS*CXhOf1 z&wXUn_piu2wN?23hW@ASdEIN>60dCNzOm+@RqNW!E-QNLXfImWr@-{MgRpCQ(B1ZT zpD24CwunfW;OtsBdEkJn`v%aPiTtsyBxDN=puef4{<+QfO}&w#U7eLQ;X}$(g?xkH z70vt>7HdzrRFXCH;I^6|6=cafrH2SmC@MMaLaM;TVo zqd@7d(fg=U>QPv>fOcnb+QOnjAzmAe6%5`$l|BL4dfyrC#0YLS<1;|I2rw_6Q!edLu5Ib4wsH( z4LBJsc0r49(WfCNYl-c9oJ@|B=|T2DEQ^<-4oOzG0@F}CY6b;PrjPC6kWn)zaxw#K zx4|}j8gVj1Y@2ej#t7jYDI;Lc$!Nh?D#I99axywGi(XN04dHUifsZnTv}pp2IaxDK z)&?>IY&S!wCd!OLu}!Pc(np<3M?K$;O2bEklbK_iUf-cl3ofq(w%f&XLM={c385n= z)8;a_g{(a%)8*1xLDr3vwdB%SL*~TEXh~e!q}4U}uV?9V>1-kE&83Ssq_%9oRwa54wT=v$KXp{60nIfDCKXEo!}(IVZQxK2n-gDml) z6}!K29mIl5*bxaUAfpaN?M1pffl4l2TTX`6$bY_EZ^g;FKt?adQ~O(UvaZ<9;0&~Z zj124st_$e+!^e&jI$=8nvE=f0T;}fBrfC>rAdD!+HT)Vv{m-;bg(oZSe`xW;LG##!Tl+1mnPXFabn?i2%R+ zB{fM7?KK%2F<=Uq3Z{Wr5C^6MTK6Xb%m6dNEI>=-(6TwhfG6+*-oOX=0zW|KcYi?V zbYnp0aWx;k$1`YrtgFayxgF$4qy z8Zu}ZU{hcQ%z*{41Z{y8um(0X0NG;04%mZszyZ+O!YzOf&;zZ2KKOwiu>sKP%S*sg zunf@Z%(Q|tEpXipIDziK8T0@ypeN`BTtRQ(2HY__{wC0u_Gc;I1O~0Tp|{b&>;XK1 z7oeezhBg|?{8=%Q;Srl`g`jmjoicg@QHMuJM<^}0HvkL-L4bbP-4me~@CLLz>~l~A zXpKTz{q-H7aqT1c1ipX@PzinjTClY-XaZ>U*IIxdcO5EPB#YJ>q5)71$~1r`XaRIU zOP~h~fFUpjt$_(J1!lk;SO81V7T5q=U<0&c5OGHk9tFoh8b}8xz)5floCX4 zxeQK`E8y{!U=>ILs{sSaAO)-eYr#6O9&7*`!6vX7Yyn%rHn1J+0I7hMnso#2zyo*! zFW?P)fG_Zq!@oYjpS7wKG>>nO%?=nU#3?{%7 zm;;&{&}4=tFO9(uC`<<@!D&EqN?V-&X}&=7gLc3H&=i1%{H~xIpkbY!wDqJ3BaP>- zfW~loh1?6!&`m=%4b3zZkB0IHFcw6DaUcpz1~Fg?hy&9Bje*Xf2XF$kSSGF7+!bLr zK*M-Tcw7&(ipM{FpbnY?WzY;LfiXx-D{2k_v@T}==nH&-A8-ZEpa*aQ9YH714mf}g z;0g55(r+C=M?mw1&R`?r3UP46Kf}KlpcuRarJxMF0)HHcFH zjX+~S%h1w9=R2T@%nwinszD8)OC)JP>+E7JT&V&;OBF8$lfg6)ON%wfVPiU=rkDX{ zf!SaVpw+csf)Y>)%D^k|8oU8H;0Cw=9FV>cozflK9>5tSV1GKG?=Ox+=bVag25`hS z&7j`IW8)#X4(QH0Js3L-j(~Zf2bd0qfdRk==zwp?{1ik2BNS{5S_2b6YiNg{=0ia+ zpr3Q6^;~-cH$ZF1c1IuBgfMK>EODLj?NSdWL z0dyaM?#q1h{o3n#Z{!90BHmxqxPEbHHpc z3($pGYd{zEGrDB@%t8Xda40L%*3V09j%J}?zB}fO|LE!b!odbTG zPeYf*q!&8k|5_+1as&>5&nQ`F2eE=*4AQHYmTJ%Eiv8oDxe$C z1C&ToUVopD<2^)q$!O}F&VUNw`;nyKl7<+d{9g(o1>FE&SZ8dI-%As`qG0W_r0^&DN> z(ed94@HG)&o7$e_B%^|PkB}Z-PBJ>%P+F4nUgFZm|I9=ek9?+9*rsEdSIR5k<9RtL z^#PQZYEAKy?&Uk5q@lc#;D!f5e+7i33(-?m^vBnCCV5M z=+NS8O-BYD_tZz)0xDe6aNbL#e8ZR!J~fTVt!n~MaLkqV&WoeH2~mI|=}k{QSt-Wyaau^Yv=Iw_qU zpu$NXr6rww8j2qaD2}!f#{YEdI0*Q{=@_OlpA2vWk_M2$ykbdDP=_MLeEbA1-xyBL z7fgA{aLPN8&i|yGx0tg^bV!orBqzlq0cGe2$f)`TkwQMrFme@nf(q*d$P-jxH_!$ANjVuB4#-gIlw=6CJMT?8y1D~imov7>Ao7H0xodJ6 z8NnM6ifuBW2cS&)zyR=>s1|=}f^Lvg?OXuqs^28M+j$Q{Z~RZ!Q!SzZ6+9hC7B-RF zr-Dh&hZfv68AJK_INGNZ7M(vx)*6r(W&%m$XHff74JnZhpmTr&pthk7D4CcqfNI7o z5@4GjGx$S|Ka@!)8A3WJ9q%#T3luLTxAPq>65BKkj{|($(J?(6kdZUMpOn(Mfj3Sq zEugW`K|1glWPHJC-1aGiC&3Az25Rx4G)5;oG65AtQRlz~a0O(8%c7)>NyOWB zS*%)Io_!8t_*&h>_6-in9DHr~0&a_xwxP0krIdthxDTkZd?oK;J4=+j6(xB9GJ#}L zUTc=9f{poT5{sSD<{>u!l$(-}2g%lakS)^Co;R0w{!2s^ga6eCFA)D&WapUY*enDz z4|oa+0AI}~oSfgMG?LrTuwNu0lfiZ&sGlwqGEu!n7RSHCZaH`jN&(*>)W6+884&et zgzkWslk63s^dzJ34Hwe!@fJ`op+@C(^7(n4D9=a~IZhg4c|7v1W>bDA~XkPz!cE;->C9!5n2OTY-2xO$_hJl=a}vy(~ptUon-zl zGTlL@SGVb=PG>;3N=AcGARLSYBfxMF2FUPXfC~2lfuJu40G_}D&=-e$fu5iT-Hmp} zMt4BBs9gZvl_sNH0k4S9Oc{FscR<@Dr!@ZDz8^wwKzFfy0Nu%^ecGnv*xv=_gG8_sECkB{ z1F2vYSOJ!TC15dF1Qq~FOZzJUrJ-%gyIi~-|1BU>zU@YXB9p z7NmeAz!yjcQCfbV!el@?C?7AUG-TjP>N&K*7rG7GTLBfk1#AYq0h_SB5%7ge7EEb) z<-E~<+b1J`rl+3sa|hcy0Po`c*xm>B0&+c_hj?CLzZ5(LH^Fsq4O|6Rz$I`ITma|6 zIgkZ1!C7z+P-lrhgN+k_y5Lc81RMs3KpHp>j)8PQ@gzG5PD$*a#&!lLyNoa!P=Qo1 z8Fd3t!8w38<~Fu->BW~@*mxq|kt#s=2s{LN-~qS~D9t@Uh2I5tfTRKW*nbQtZwV*_ zFF`SQ28zH7@EnwZ*WfL%6QKR+*A}b+-AJWhj-Ur-W`OQT>j83Q3xw1;H4!!kbPKo{ zPzFtb5>Nm#KnUpWt~97co*GaMK7uMx0ltCvAdTAp9rz5sf-m3`pu`^l$!VX$@8Ach z1X7%Gy3Z{O8Ui^W4-`QYK=;2J0~MeS)BqVy+Zur6@$|1Hpob$`KpW@+13Cy+x7tY;zczj7gJ;}2q`n~ zR&u8cAUBdScWk=>Qqmit$c5M@4^k76>&fk|fLu+kr%I5!dmyAHrKbR705zo}-~R2e zLyrik?WwJJOC7M^5%BKpgze72890G%fQ;#ikRB0`ygLW)6{-|f;!j@bN!QAJ+mKa! zyHKYh7twPoFF?mCeMm;<0&j#gX!{~O3f2L7_H`H>0(-!2unVjQ13)rZ4andmgi}F$ zAN(VwlYl>11y+JE5DZp;z!KNt#v06j3Ff)l|!Fa*p6 zbHFq(o7#UAHpYQjU?!LhW&nD67YoSBDIf++1W{lD7!M-B2oL~xg9l@qjOYu<05Xn@ z9tb3jqWvM%{(P-SK#4=ZFu-Rb8DA?(OZ$8tk`D*usj+|x90|sN2rwE@1;YWwkx>Mt zr*JH_Kke{VlZE6BQu?P_@$rAkLoza&^3(PtKutw5st`2`c~>$np5LLir^LJxY9HF> zt)*I14dOUCDJR8L0p42)*rq~wos^z0kYv1Jq>J*RZ^Vn9u2CcjD1!O`DJR!T%6X+^ zXabfA$wY<^yw0_V;|=0vl!lC@ei2OW<0HrbI;{APNQV=-lkc^37;ONtfbx(Ma`8q$dUzuw zD?>Uq0Xj_kf}Mc8mI`)&cxq=l^x^>Z+|3{WYzN!GRD~vZFYt%1q79g)kR912+LpI7ogU+xGwsYImsJZUa7Xdv1F!BJX0G#`_|K0`LSB zf;{j5m;yua6i{3~cnHQL&m)A7K>?tGOaNa7>2d>9ZafW*#n`X~F92^bS$hNfWHBYA zG*k;xVh1E^Rf7F8P%06=!FB^&a?|TZjR3uRM6V!810j$HKdAlXut9HWe?$g)tDD~T z762(w3t0`Q29@ADr~u!JQL0EPT4lnf#{LFqpW+Setd4$=*^ z^#Y#24bVlFsIAbhH0S+V8PTRG{$4wiP7 zwm2)Yw)#R%`|(Jwhy=^q4p?k_taE!Lu(7eUx3m&He`|!87HUSyGYtA%M~to;Dt@EL501df(A$SIZ14j};+47`Rwo9<*ZLAo#Ar(z?f?$)Iu zEUheU==Q~G1EGd}W0mLh>Y6j_UJ3TPsx@5HxL0&CUIBgW3M9cAsr zJPm|1@Cy_-4PeSRXi!7C(2^-R3Y*rBMLKJk3RC7Vp`p-Li?02eBEh%mR~v3Ub!(bP zk;vAK%-0aM9%Q2tXwmfnnR3I#`N)m@s5SypBuZDnHX?yN&3bWLC!trvF{VR1w7exs zfF$DitrB8x&zDoWc4dw^V&Fzg8*8cNtelE4XAO;nwphrdqmfWkgSvQQB)1OvsQ{{WHJ*`mzA7)`9)MQUu3tMp8T2Im8C{O9m1xoktHy9_7 zz6l9gAWp1JFE*0N9?I@ZcGgD9hzzo#mf0)AhMEX%P}L+8p>4b0OYhRz%F?=>rK2ov zq7-&iNE>F|Mrdn8 zQ$|VjEtTUc-2~Fm-5$;G-dw24d}avc;QiGL zh3dF}_s?lpn+jV92Ffw3P@#rkf*i{<7i#z<%Za>#)Th z`lT*fp&2r4oW8cywfSf!kXC_&>f8k(HAtry)z=T7TsjFggoKLsVonx9P46Mtr7o>} zhf%4o6XQ=56iRn zW-#-TJPYoK@S*%_EA-$REnu`etJ>E1tefU#R4Eii)6pHLdI~aahk!|pW;ZKl~8Ys8fyZGvULRnEtYwE@y z6xkc35GXWaHQord8!@ZC2-`Md9zO5{=h5zsn4TZRL5@!kd~L+0k3*=~nE8+)ts1i@ z=wjj~Fq5rwhyw35t~K~Z;v1PSNL+W*KPTrEj!zP5uy|{D;xF3%qJ<5%6spu$t?i7X zg}Q3_%&quNX^?`*3wD;))>80SM1SFL#{BG=8@aUpG<5Gy8Q{Q^2LFU+zyW z2~AnriJCGetxHO(WN5>K`nEl4dMuY+W_E z#{UN`Vgr7&>!))rtFqHW(6}f4>KmjfFjZsk{eJ5{^&3-gMU9OMMBTW9o3Ao=Yt}8XSLXS7uaz}i#2sZ$W31a&$U=Ewd!Xr)+HFB z48Fz}gh84nHQLSbDi+O;9veDW^ejacFzFp+UaN}4ew?EKRE ziMPRKu6$@0Puv83by!m`{#<8f$PZwDnbJTLKhTQv^ZiQP6u%FUTrH&~Q=7^0r7%4s zp&EPIsc!rh@EN$%isZSiexLs1ATHqMw!f69`9Fqse!{}l;D6Zmt4gl-{a)pnE!n0C zXaatc&Gk+$_umd)ZV0Gf$6w0)?L6}ro%I{zpN<=@8}r(@DTSby9xIIIy9R!aL_B*I zdEJ0-!N2#DKTk-09=m^Rrk_W&+|l>6Qa72&Jl8u}#)!VqJZ9(pu>8KT;OzGkqJOjo?`; z)|bLm1Ga!dzHF<9mj4$MGZs$C}9|_#aQNX*OpYZY* zKY#H+z3WZU5!2ZA;<4hn!}GyQ(~Yad=X)y*1z*J%S6t<_i;$2`>T73h9C5rR zD@&Bno=*Rg?)ax!1m}xi&M=xkujBZt)jyZ;&BgZ}-lM;s<@jdeXXE@;<6pG>Vnf|o zjz5#~Wr=fZ{2cEk$NBo5mTx+Ks>Pkm>d);Z9riCeD_`6C7V%SvdYzW*H~iH%chvqo zvHm)P{*R{c4Z#hVKhGe)HtWx_xbc*q`0_JO-lbx9nf?nG@HzNN*PpA*&wK?!6ZUw- z|5t+u?{;o>!*!egarc66b?)Me@0OCTu0P@m>i0Un&42CT{KXirlDm`g*I5B?`mb8} z+$L?9W7yv>0Qt)%}k?VZUF6^LLlI%l@A;BHpU{hd{k!UhD7ukGrbiZ%6Xi!TfQ^ zUv_fW?EkI4|DtR1Z73cYH25~;ZgBs+(&Nrq{8bX)wEQs7-ERGP^zhbmm+Sw>!-jWi zeP{lC*cUrf`|tOMdG~Vj0|7T^|H;6g-JL(${`b+#Hx$=XwEuqhny=1(Ki+>mXzPzu ze4V(X_ow}Q6LS~)eA}g4utm7`|5Md}PJ|>+3-xZ!*4xz-h-WUpUEOiTiHoV5sqjv% zcf*vohMzC;9Zd8f<2T*>5dJ?NaBvOI>*ep}@YAdM$1``@XPMn3&Y`@mzjgp#CEuK4 z6aU2_SO4O&-h80`oaYbcv42rWJ)8NKi3KOal|i(w&_B~20@6YQ~O9+PpS>lmJV;mK0MlGt(0 z(xdcG;>jj1a_N(4%@QUH*9fjzGwm2$w%)a7J~6mLEVO2$X#cG>+kkzo3Qj{*viytp zZ6-eyNZX1PND0}k7(6>~gnMChA1rC-+{)FTDtgr=&}A*B2tB1u*_bJKu8Z2D*<(5*%o6aF+gi^;j|$L*R(d?SXp6$S@heiDaD~prII_-D zCG^alD%2&TcTB~@VqKOm5hp8BL`G<{fGI*H<~L2KFO9YJrU^YM%Y|t|(|+CI5PHCP z4W{5(V}kmec5<5yg`?_B`Q1nl~NP2&4nJ7*pmFD>PHc z#N9Yb$W^)l8B}4-R>k7ZLJICSQr!I}?Z>L9x%U=GBeCeS#gSFT3T^BS@qP#S;Co~J zV`|+ty%$KULPB>3OA(UMSzEWqMY}HkDv-t%qUdAELgP?7NiS>tUV)!pCg*61n?_S$ z)rlF;K+}t#Xlvjdo=)Ps!CuLM!8=}R{6I05_BO0=fl!^jZ!MJRK=({WcNW*9XKSVG z*S*{-MH3qHY4>$uMB@eG{OxxU?GrqEnr9#ZR~PFuUM z!lmel$Gfnm6yEN_A{L=5mvzCryolejZP>U=r($p?mLBudJsX9tY}{g`(C#XpJMZmv zv`1-cMZ5{Xy__Ls)0OR=30$U_b zk91|0vtVf~vz`r4B{JzbLNmkL$Y+UszuzO{W4IE2&DWWa{d>N@Tl8No;QS}Hz)YSm zTBOEIf!{ss#C+!P_4{iozAdJAXI2aN)VwzgcwK+5RlPzv=Lr6&uFF5GTj?xrXw5nO zopodf-$lpb?%(ZlW=|5)Y`no-=iwV%z*UzoS#ZXgna@W@xDSo!@CjAv%Xi&38ojoz zBfNEH(|@b-zvuwJ)UD3nzo`4GzyH{K{zaktT-fym{1L37UEv~r%&^N_=hnIvw@<-O zc%y)h8Tp>8lZ9%sI7sWZ{$g?cxXSA-9$vJX_NkbCcxz%^mH+Qf;hn*sP`O@aUy8Eu z6fB|b0+$;O0gGqXmG$>y@s~dK-yO*Jt$L31`TNN7!cA;s>aoyn=bPNvRc9o9krU55 z6K20`s+ZJISx^`A|L16=<<1h8<7hB;XEzZ3=e|A9on@{<{tfP|Ns`c_p=kU(;?5R9 zXn7u0qPb${x!&1_l0E3rBOObah{a-%rHKgtuBWPWOGVaYwNOKWe+8X)mB=0a=neWm~YL{^<;hYD4HUX*j{ zMNc|@z7{vYM?3&mWEN*z}aykT439rNv8<4G$X>I zqNoAZbh=%7wXyKMsNS={mg8@V<}udM*4!r=4~U#=Mk~rniu3zAo?= z$CRv|_;GXBuczu_RQ=gt%4^J}XySC4$FpmvF4b9*nacg!NEI57logCgDrDnFLFE6JEHR5gXT`i=b zap;t)y2_xXDKllH(OL0=GQM7d7;VHHt9|oPp~Y3Bx|nTk$KFe*i)k@f>{F#@x2(;pOs-YcMYTf|WlEf|wsk>b@5Xg8dxx>6--N0S z@Rb9pv|%g(4cqR@FtMU@{dHP&%nsckkapx=Kqy8G9oo7j#!)8^FQ*sr>DtB)U+3{< zH;`BG2YmvgFs8i`UD+~BJcgf*Pp&F*8ZZR8=`@G?4(-F3&qiT_pgfG--YE1Cj33VQ zHeq0i8!j$yr2UW`ZOzM*kb}NmffM7r;cV}CbjS|ew=7bGZmG08n&XV3q zYA}LrAsx*}i0j^Q!IsB1t-jM!Ya8+RE9^$F_oSoENU>dC$F82Ks<6WVDR9=IZhFy= zE!vD)9UsZMY=*AizcL_t-5mDtDdoahqn*fcE}Uf|r*=-b_$HFe`Owgu)}JPbY9RXB z!=rHanRI?1B|eOXjyLSwO;O1aDXdWe=yV;;x^F=Ww+L~HJ+1n0shm@5C`uukFZ7FG z%eJ7!ijjhD*Sv0|r>Qv8$Ob7;2I<&2M%?)VXT|7t{jtxYt}U;RVNWRUC#0a;1wYQr z9vh(g_*h+vR%696ljY8vSS(FiT^Hj$mYHuw?V^x^y0-3#xTvc2?eG#A-&5FF7ECEF zAcZMXtax#Ma(dnsTn+On&{GcXhNp}p*?y!HpuN^@M~;q>;(7k-QPbwicfhwor151B z8~Rp{dnBtOoe@Yu$8_M-Z5dzGp6usS*hnpmWLDdtD(HI9q2%v2m&J zmA#b7L~)wn-l2;6^8;`<6}?C_4e2_ObxMWjyG4tKnxdMV{U&yelaYe%Vxc6l77gvL z&_SpC@5*7LYwU7pUTlpoZCP9Kw)p+8Hkmj{eC8RP|K8GTQ;Ye?jkdSLt0>D5LwX~p zuXM>zHO;Au;g2}JJ<+ea(Ge)FyJ`cfDLz_0gDp6$eO{M9H9{7$5j(i)qN2+=FGK5?|Mdcmbg)42FJH@Akq6h%GB+72rovAw&576GCj z`1cuAeY2WM51b-C75r0!i%!*|1CT!u|7*xOn3?avP43_z=CKEUYMLOf^SOAhqR6MG z26HvSVWgA567~p9WsQ-dB|DTXw3oJKB?we;IEk+9Bxjn;Dn+O#aPwn+DY!kKz~-h1 zhyNdE9RE*e*8vvQ@$F%EP!SNMm!;avQY=Jb0gM$9uwYA~F0deGrR;)=8bysQ8iGbS z_7-De@2B4D`+x7{`{Lbu&zUo4PM>pU7haiGUvY?# z>M9NqQeDL%LaMDeL{N1VhX|>z;t(O#RU9Iux{5=DR9kU~pz10P5mH^nAwsIFI7CQw z6^96Uzv8GbRvaRzx{8BCXx}2GD%QL~o8KZ_pC;x7^%&!Kwc`4IEm;;K*b_Rj4o$vy zbax%zz3iM$s`aqMgVRNSYBh2r!#RJajMn{xVM8@`w!O{h@hQJqa--aSuzzw!5xov~l| zzpJub^DKqs(PtQ|kOdNxNBOu{4FfG6%13-L=IO|7{g0VMH#v{WwpL+UVwM~>K2g~^Ky(A8=OS45~#Y9Cm&5b#>(8S|QI<^JICdtWu zP5~HJHJl?_!|vft?Q<2u%K>=-4u=T|?^R9X4SmN;FnwCUvx%aOx)$ zO1CM zV#@{5&9HjB3*f%EzksrKD096okl6CpZj9@k_3?gQrAr?77uoMrdQ7mNFK)pMW3T+S z@?^sfKtL+x-?a?_q%IeFt4o2Op00tfk;0 zR){Rfg86{(dbHu1%dh)6W%4M-D=3UzHlG5GaJ4nSr~?d}ktq|-U3(`=LclNr!sFE% zkGWUI^lQ^um{_Ruj?JfWM!aI4LYs_ASIdv+h*23AT62LY(|>f9n==AB@p=w53*T=! zNs#s_Wl7_v4X+Off-`S&`7WU7UC>7$Fu2HWUFlVv`p{d<<3blKZXx9YBXrP0F>6;O z4T`_guCNnmNTt0N5MFDpPY>Cyd&KvTvbduc(MgO|)LcNS-MC7$REHOdMN9mdiuz;H z9R^~=N22zzkNWHvaYURnA|LnF?V7kXgb2H)rNC4jOcnWy2|Bpaqx6t7Xrd0~l0K12 zr*|t`#4Uq7`QCA76ZUDq#hK;6kg8epTB_D81Ut=KPJTb*i#6u8G_ zlo(V5&6=MNR`Cm{s_*H8c92=AfNDFLe0hzy& z3i(*`e6GM)zJh(dQx7TLoyixd3QCjBt{PLp_u;u(*JCVqt;mTpMn21!$yYD!;LI3N z>tiqgYm7MH*!KC5=s7$S3GK#~7I#;PtFumTcjQ5@g?KqR9$&fV_Mb^te!{g3` zV_*9*yF+6GYYB`B-7mk@)O@cpy0s1%ocPkv#Mmu|Q?h`8Mke>ZY(Ti^-u@n?=-l_@ zba7g~gtxR|FT!T+AL-~`WwOG5Ed}pWy0;8pD|Ul!Ouv-1C8kuI4BP};3;=|Ogsc7@ zjVU+%Rv(A-=<7N0+IoQ#!B@UsTd-g0?!FWV4nWx7 ze9^_W@%VWM(Gl9vy(2iClDi*?b>muEydU)fx)x;^;5D?-$-6YA5C!}T+Kr1sv5p=UqVlp^Ct9=f%!@bUj~AhDAVdhr#%mq5 zJAh5E{-DxR6#+CpN!qKhmpzB~_5zyp6B^O$C88yU4(?;KSK z%wX6Y2ugP2l_p`tU%1iC(YMDK0Yk8xCuDOFEWW1jgOI&)Jq~_Q_Vd4qBFH z>D3`+PgT@5alnWsJ_&2*CZHe3OI*0J#n|nXa9G)5_}|+_E1W+!;_%sB1NhDsHYn%` z8gzol?rzMP-T!9h3qYjmSCg2oPXmn$FTPFsdHJoU4wln)h`HB$r$|k6(7&kbe2?eC z7@?bm`Q9CdLZmAN7vU)NBkECvGgb#^K@kLeo=z0Ob4KkFP4>d{hVMFjI-T!^xg^2# z%w427f-H8JS|3sN^qTUsXq7XQ=5G1rVhQ@oJi;SnS-s7E!&U%4W3&?GtNKdT<19M!4|_ zW?%47=|*0~N>{5e98CzJvO=ZGU2Dk9*6MRCerx&sp53;_T47owR*C_XmKG{KJyUe) z2^yWjB5>cXz>X~%pXMm&6(C|$8Ayh~Y%2ej@`ai9ufAU|mIz{fk)D{PPfgQ)9p9vj zy6n&+43;)7YxmJ-uy&dGP&l<<^{!R}y<@y5!zkMXwnpMajq>08L1o?_L|1oLy?}w( zK)g$*?)l`Wf9mkx*5K9%7Z=^wJ+_)4fOZYXFz>nMU0;kCvP;LJ1`>jjcKrQO`hhoo z#PmFzBO93uSNMfWu*?Hl3*ENAN`D=eBC!nHDxwjO;N4RAirWT#^AcsNhi{JqPW5U6 zQ7Xf#jkUtht&~bO(mp05$9u{_d@qm3S~z(tDNd`*2U;ntaeImdH_=cJ&keqcn}d5j zdW4^z5mXG@4&5)-2py*PTKQ^-j=wzPRu7S$-6|@(xXF;*Q8$w9r7^9@;jpsh1VlRd zPfxFTIY#$9uLp0hbGyM%ZjO+qMRNad9(fLo;e{ho1dopCX^Cjch8SIwD}utrSY)MmVj_26&-u9WBiclOcTUBH>32Lj0|+P4lllNveCGa zZ}6^3oYA5>2;(pw1-kolzr~#Yi$g{{QM%L=5<&G5V?y;+0vGIuff6feBew4UPhl6o zOU1*Vnc&Lz6>?OKLvW!<4{@rx=&SG~-&*}i}MM5IQJjuo{9gr}JMHBOehIgJy- zAZ!SPs@XiCLgY-u6jSq3gl$bR4J`#TX`sP(TQi%T@?6zAmcQEIb{Hlb8&Xa&ZRD8W z0F$SKvERo=X*M1oCSk(L7Z=mbQf1TnJAmN_jG&`pHQeoRd)F9!On*rPz>GLb!N*Xc zehmzFV0J2c8y0>{+NKGabtNFYd+{XqSX|SFuXqv9`N7z|M`=99D$X1whcfVU8JOJ8 zOMjYXf8MI=6$uk^sW?ik(Cm(QkJ^2(ZV}TYF?pjiC9HbhTQ2uYYhLn)s z4O}BiCcga4_y3+gHd+`c^x-vt@W3`W=6av|y&6Xf5MdKMx`Z~L!lYAx z;R+0Uw)EzZ7AoEs;|qZ37nM-iaftp0TrXg@y(RSIEUrgNDDDhuvbYl{1W%Qa?gSoQ z1-YxZJ}jZ-{QhML4ZeaaE2Xn1U}nyx;xz74jxN|YV9g)WG>}u%Qp&pz%$B9p?j#tB z1ZHh8bis`R9dna!nHcI(N*TaV3@oLgzXLM~nA{dZqsKH0T=qB+n0!lzu$fs(`#9#b zQYyR&O!Kxdzm#l$1;z%@;u_9~7`Lu)Lfbmhtgy|ZQi=dZ=rv$C0^`hsQRn~gjNN47 z>nR{y$j*_iJ7ypJ^^ydEW@{azJU-UBZIMO|boK`$Z%5U#o>wr<0RoqpZZ zL^a|V1>eRR-nXM@#o}Y+cNXU%p&>~V3h2_7i%_)f~WKN<*QKw->vW-deN-)LyY|tH`s;tVy;&adF2ffaB`6pbG7?uT3TYeF` zJOB*7EQ_#bBUg5vIo2doSzyP@=$DU3X{Comj9 zOBp}v>pKN1oL(2)17$~3Xx$nR=d-gu*|+-GLkR-w`}`DDV5}r|K~tjA+$H4+L4mU> z-S98G@a*zW?@OcoGCZUA$uop?yvivH^%Z4MzsCN>w zKtX0(q7?ew?_%8FY;$qM=qWqTK!d!vg3$k~6{{X$EVpz0yLGRByE&_Ypnaj4CYv%? zEV~1~1Y`+R5wh@HT#Abo*pZiBaS`^Pb5Yy{+qu3R^kmj`IZMDIWlFL- zWMRm0Q{sd=mWdJ+*!vJ!mMoBCIQvjY#AP^w`30MaB4Ft0B^r85>Eeqee11ywmgNg- zhx+cG^;q&21Ton)WNxMSMmAUQo*Ud$Reoh!`EaQ(rF3HkUu8alW5j!E64&d>T5_q6 z<0(y61ag&><;-O>tVSj$=R=uM*$QO)Zc%C9`>#^&H3-;DnS~sozb4w+@L^qFj=J9V zXF+!SU9&|}DV@Yv)l^{cOxVmV1oUhx1qQD#6M=P_v@Jby|8IN&mxf z|H-$kFFAjk8zBf%C|jg*Me#v4QHX_x95()=(2#2kmB$S+i$(@2hUrpXsH9aw>QbJs z^fg&!5Rv3|p`$RE*Etqtafkou`iESAz)H=Bf$Eo38Hyy!aF-VldP8FHXs7+Z8o%uj zBQXfWF<*yC=I)DCgx?h7(byM%st*iVos8i;9wCZ~j^wV20|u`&0_XcTc@P=ED;}v> zOSwsNZ$WR7rF4{E6VZyZH)%ZT4AoU&)&}Og&%fsdrIYR)82!#1kz;XEo>;HD4lzL@b9|$xT8t zk+QiB)SJ+y$zK(I{2c)A!NN++2i5-~NQ{LbGwy*;SxmC4e<&XL9!nIeH8~-O>x@>2 zj?_Z67xW9k?|@&u;*reh;@<%ZoShW5R8+TI>E&*&u|ZACm7$8?%PFB8 zR^d`1lE2!zSI+02OQiNpAb!KZw}Lj8<3wsRVDJU537ePE+vCq=3{2v8gt7E+K)9VW zx_|7%WT&2An;`Lka0k5bU^>;C^V10vLlxL#vQ<3@Em%3YSJvDS1?;Hv} z5Q~oh;fL!Km#2=KKci)u3DOS`KS0ua{@Q=;eEl#JWF#Q<0a@dH<>cP7(3FzYFogX^EJMCYgr^M4%FxRs)l{{4XtEy>@@fTeP+qM2Azeo7V z?f(z&V*Ag_{Lm@10{ubDe`pVH6G|<6cY{u27zVLFTw?9qMElK2$tBRmMXMKIuGOb# z5x1VLc3iBOlPnmt*vd&t)vNhp4Tp;^9h>)oJrX zfN&=`d24UR{JiIAcMJVaIM)<<45qxyv^`-KFR0#Q^t3h4XuxBn0`a7kyXw+2(IeXb zRk)?pp*81%xBWtJ0^vCoK88scp3_A>LOM;`!n}OUnFW`GTTP)hk-O89p5#XEgmy1P zTdNr}c2q`_M;%ReQ*Ey`ga?$q7{d?C%r+i6lo}8+-89A=Blx=sFtuvzlO{Y=x&+8} zxC`3k4w!r)d~3YA&20zEOo=^ip$duj|ApsjZJmce3-iMBUn{ZXKYJJjW(^|||hnlPTerZP^Q|Jgqw{k%@u)8A_IYZHdO1o>fe%9*K(J1{9fWf2N4y`_-_}biM zCX5kpXgQ}o9vF_mxPN2twMT_EFl3o}z62>=KUv%CZ9<+2vhoc*;?%bT!wF-5cfP;p zmlvPl_ub{O#cycebIAYV8*0a|kKRxo^q^3_r3~DMy1x~zyw%XFTORMdUf(p*yews! zQ~#yQT&LzW_TRNGnV3SRDPLti<$L*BIp+rjMr9dcfy1fzo`Sj4;VrTA5zj^QlfA;( zZk+e6JOfO#UMPKBZZS(iSsPY#3e|jheZI5hd1gubUnqTT9DJEz_!)kT?!QomL+Xi! zku8QB($hBExBK#3{IYpr6!uc-YuSwkikBRI38Mx4(vNnFmkZ+M6~A;Nk5>Xp*ehjt zqb}3LP5f7FS|7I@UFY3$KXFE@hE~GNwcBO4>EIVaL;djsvb$a>6O2t?E1z2$zk91( zgBQid_W#1sabPynlEu;KJmyD36|4r`wPapoRUqFsRXe$ zqe~s5O$svbw~8s$ma+O|sbr0mNoh%>QZjEEUX$56MCi4e#4NQ&pQ;HOp-s!CJSFq> z7T!FnHM)#pn!wgoQL^+}Evc2vost=IpfgHVk9-)bNgI{SgB~eaFa=pL8yc!)O{j;G zxlo~!dB$bsWM~aRS(*f$Ho`C}F=&`pm!Z`g5_PHCv@CUcnl8u0hWN>lrODFja! zJv$ajwl!EQinC+(r1NFHL;@GnGGcFhmV6v!WPXKkpkDU`4F$3%ne z@J~V%*eLS>8%^r4e`tt3pi3Rt3_ik|uG=vyiuPf%X`lnRdhErfkdrH$AkvHWW8cvl zd)C&y8kEoF@w;KPxN< z60c278nC*wTg5&W$2YABDGAK~t75(Rv+m;U=iaO<>HMKQVeBezwvw*+fJc~lZMy9b z$@i_t+~|t{aB(4kwWAL8m@EF63v+CeqEAiY3ax6MsUdCBs^04~30fW52H+REwgo_g zZ34j84JYPSOVYT3tm{Eh-TCt+^_Wegs-q2Boi_13`r;Ef7j0<(`~sS95zfL(CnyN67fBgT%$D|8!Nh9*4F)%Jzu8>_`WHS8 zZp=CsI6;CLO`ylqF0gF~z7dft8%q(*K;(f92Y3~j2mS6RV~)Uc%&9nDg`P9 z9*mKXJG`N{CzQrX;gL7hbb&DlgjqAzfnGLY?P+;q=4T_sfvohD6rGlKH^xlo8-Y_t zH~h1=0qa7on&1<8KnMLF%RJxnV4dl1L$;7hFp63?g=s1qvSl>UnKh#$PRxFu9v&eq z_nI^Fr`J9bFO=1k<KO$18L#rCHPv~I) zEO166uu`m-S@CELR&cN{g6`I1(Ve_W;ZXVYPavSV z{8Q_25ci0Hs7k__F9k)wl`n*YweLP*v9!J;%cF=cU>B6}2RJ%Hi30M{2zHuN`IUx* z18UisCDF)O%!V&IGFsA^ITUn*ZQ?_)O@v;Plsa0Sof@Pk|4yta<#%C06wwJXecPGM zrMQluu%#1=!jLf9--R_qTD#MQMd6ozT6AR^YGDh@z;jzF?25JEHh?BXu{zYP8|%my zd6ho?1e|@{jg?bzcjUu}?uZc+x+6j^d!wvj^p0W;Ea`_xE|MOsMvatgjXnwap$GF4 z%vwDxH7QA(Mo*)`)1)HkS;(cMqhT58(Fg(nxY3ZF%*B&uokWdJm!L`fLao;(3t1>N zNt>wQK~Y61F>C|*#$aZFM9-d(lT54*S@i_N&av=k%0IY0e7#gIgc*6Y@kIo-v$A@i&Cx69=^pX$NtZ#4=AM|Bn0J zSoS&1aYvqk0P9jt9HxC5g(dwI2g2O(5J{ZG0^)ic`(BB!fYH=m!2k1|(u+D?K@3XB z0Dk{L%*`^G(g(2tq*w`O6O|E*?Bz?Sp^n#xt*~kAvhJVbF#?u-3wOrmFI^bCu7|^kqNRM1D|7wgz48 z#~R3w1xjuEv-e5w?+?Xp>d*XSG;#FUfl!C-02UxW7Ditk0D0;Lm{uS^wWgB;Ox#sr zpxr=NU-N;?Tc%StRa=^~}$Dgds>B zG{T^6K?g#hoky^kmh=mVdq*AGH9;lZ?;injt$m{h|`s2hd%XU!{b zBP&0l=94VQM9P&?2e1Z}DEPUG0rEtk+_n-RjBQ-yHl{Z;a(y84oac`L0|qfyL`9!m z*4eT(tq6r;y(h9-#&2@jY6bPX28leG#_Wv=lUcMK_hI9feAe5_vL!_Z1Ghl|bku?@ z6_EVy0yazGiLW$-aOFm%XK2&ZNqS9kR*(+KU8|>_3lPV?Pl8eXwt&SGEkMp3ycEvZ zY9Vu0;$RR(EoA+`&5DIA7ZIQh)nCNA)-+_Krlo21#^gopBTHY@?`r7@H{7bXYIL^* zR%pp$cG?cz9nMI0T2g9aDqSlFyGxcJ71jM2(W3&X?Di5w)0Cx1QG#Oxt$>#Q&1bg8 zy-V3$C2Z)2m8?Teezu8c%j-r2N9*rVb2{!o5Y>Or>KlLho~de(<9+tA@!c9$t_1H7 zHn4tFFB!(Me zyD~jCC9`WYmEC)A`gRX1=L>{32T8GT>*JxJXUhLL`j`)FkU4ug)O0i~m#+#yQ zenzw<=Qr69h&`SzLz6X(|F2Fps5J?ObX|59Z5hr~^*(s01%VGAQREKRz@rKw;8eLI z{}R@~Nj|X%nEa8x9nTuL$%h;TfOy^!t3`dSXa4r$$wGj{4>aK@YhcfhDB=;n0JBx4 zP4F6RMH>i$$76<23J`b2D~p*2Y?iws{kDNQC~+W>PHklEVLtFsp2lH0H7o55hC-(v z;Aun~*lGK6*4imaIuDF#<&RW%ID{Iso7FL{*~M^#FRWk>D>pvf!!~$33Wx8wc$2i+ z47D~hTSF6zn96wRBAabVM=r1@WW53Io_`h5Rd<>Bf%R`Lv#*r=gtPM%HWNBMl?Q$g zTw%>!c~%fY$LJBdgzQvZl2)Ij$)cYBD(iXSgI1lKt{?-u)U7CZGKArEl`XSu zY5e6XYix<@oog)Fl6pS|ZHF6(Fr#iTb>*!ijxfRfuibzfn@}3v#Lhv!b)=~`AsYGC zn$Fzh8ybn8J*oe|oTl4}e_$f{)|yWI0hyU@y>H3Tp|==81}|^1dzDrAliO?np7kn6 z_RP4$yzyDk=cYTXkTUKgv2v4cl8}Rii?21*{3x&(m6A1vETR7|xUD)nEj4{!CZepv zX;#CD-=$%c35H5^3s_6P%ZAd^BIaj&e3wnMbdp~paM4Qd6Y!TESpz%atpuJ5SHTnz zIBRe=?Wo&r{B!s|bE?9;9oar$ew9mW_khiN4;uRbre;&lY{aKuB8`-lBNYgpi_R6; z5$-8R5N;6*feB^H&?VTyw5HjIA?{^Muz{LXfsGqyoid&=Yhz&rbFWRFFPJAxiI;`c Y{{@>_Y2#r?2tq7jm5NKOTdMssI20 diff --git a/frontend/package.json b/frontend/package.json index 5a0a287..0e14a00 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,14 @@ "clsx": "^2.1.1", "csv-parse": "^5.5.6", "csv-stringify": "^6.5.1", + "cytoscape": "^3.30.2", + "cytoscape-avsdf": "^1.0.0", + "cytoscape-cola": "^2.5.1", + "cytoscape-d3-force": "^1.1.4", + "cytoscape-dagre": "^2.5.0", + "cytoscape-fcose": "^2.2.0", + "cytoscape-klay": "^3.1.4", + "cytoscape-spread": "^3.0.0", "d3": "^7.9.0", "javascript-time-ago": "^2.5.10", "jotai": "^2.9.3", @@ -44,6 +52,7 @@ "@eslint/js": "^9.9.0", "@ianvs/prettier-plugin-sort-imports": "^4.3.1", "@playwright/test": "^1.46.1", + "@types/cytoscape": "^3.21.8", "@types/d3": "^7.4.3", "@types/lodash": "^4.17.7", "@types/node": "^22.5.0", diff --git a/frontend/src/assets/loading.svg b/frontend/src/assets/loading.svg index d7f6f98..60eb32b 100755 --- a/frontend/src/assets/loading.svg +++ b/frontend/src/assets/loading.svg @@ -4,7 +4,7 @@ animation: loading-draw 2s linear infinite, loading-spin 4s linear infinite; - stroke-dasharray: 1 + stroke-dasharray: 1; } @keyframes loading-draw { @@ -14,12 +14,8 @@ } @keyframes loading-spin { - from { - transform: rotate(0); - } - to { - transform: rotate(360deg); + rotate: 360deg; } } diff --git a/frontend/src/components/Flex.tsx b/frontend/src/components/Flex.tsx index 53d63b7..48ccbb5 100755 --- a/frontend/src/components/Flex.tsx +++ b/frontend/src/components/Flex.tsx @@ -17,8 +17,12 @@ type Props = { direction?: "row" | "column"; /** amount of space between items */ gap?: "md" | "none" | "xs" | "sm" | "lg" | "xl"; + /** vertical gap fraction of horizontal gap */ + gapRatio?: 1 | 0.5; /** whether to wrap items */ wrap?: true | false; + /** whether to make full width */ + full?: true | false; /** horizontal alignment */ hAlign?: "center" | "left" | "right" | "stretch" | "space"; /** vertical alignment */ @@ -56,7 +60,9 @@ const Flex = forwardRef( display = "block", direction = "row", gap = "md", + gapRatio = 1, wrap = true, + full = false, hAlign = "center", vAlign = "center", breakpoint = 0, @@ -75,7 +81,8 @@ const Flex = forwardRef( direction === "column" ? alignMap[vAlign] : alignMap[hAlign], alignItems: direction === "column" ? alignMap[hAlign] : alignMap[vAlign], flexWrap: wrap && direction === "row" ? "wrap" : "nowrap", - gap: gapMap[gap], + gap: `${gapMap[gap] * gapRatio}px ${gapMap[gap]}px`, + width: full ? "100%" : undefined, ...style, }; diff --git a/frontend/src/components/Network.module.css b/frontend/src/components/Network.module.css new file mode 100644 index 0000000..e47c73e --- /dev/null +++ b/frontend/src/components/Network.module.css @@ -0,0 +1,45 @@ +.network { + display: grid; + grid-template-columns: max-content auto; + width: 100%; + box-shadow: var(--shadow); +} + +.expanded { + aspect-ratio: unset !important; + width: calc(100vw - 100px); + height: calc(100vh - 200px); +} + +.legend { + max-width: 220px; + height: 0; + min-height: 100%; + padding: 20px; + overflow-x: hidden; + overflow-y: auto; + background: var(--white); + box-shadow: inset -5px 0 5px -5px #00000020; + overflow-wrap: anywhere; +} + +.node-symbol { + width: 20px; + height: 20px; + border-radius: 999px; +} + +.edge-symbol { + width: 20px; + height: 3px; + rotate: -30deg; +} + +.container { + background: var(--white); +} + +.container > * { + width: 100% !important; + height: 100% !important; +} diff --git a/frontend/src/components/Network.tsx b/frontend/src/components/Network.tsx new file mode 100644 index 0000000..05aa591 --- /dev/null +++ b/frontend/src/components/Network.tsx @@ -0,0 +1,698 @@ +import { + Fragment, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { + FaDownload, + FaExpand, + FaMaximize, + FaMinimize, + FaRegImage, + FaShareNodes, + FaTableCellsLarge, +} from "react-icons/fa6"; +import clsx from "clsx"; +import cytoscape from "cytoscape"; +import type { Core, Css, EdgeSingular, Layouts, NodeSingular } from "cytoscape"; +import avsdf from "cytoscape-avsdf"; +import cola from "cytoscape-cola"; +import dagre from "cytoscape-dagre"; +import fcose from "cytoscape-fcose"; +import klay from "cytoscape-klay"; +import spread from "cytoscape-spread"; +import { extent } from "d3"; +import { omit, orderBy, startCase, truncate } from "lodash"; +import { + useFullscreen, + useLocalStorage, + useResizeObserver, +} from "@reactuses/core"; +import Button from "@/components/Button"; +import Flex from "@/components/Flex"; +import Popover from "@/components/Popover"; +import type { Option } from "@/components/SelectSingle"; +import SelectSingle from "@/components/SelectSingle"; +import Slider from "@/components/Slider"; +import { getColorMap } from "@/util/color"; +import { + downloadCsv, + downloadJpg, + downloadJson, + downloadPng, + downloadTsv, +} from "@/util/download"; +import { lerp } from "@/util/math"; +import { formatNumber } from "@/util/string"; +import classes from "./Network.module.css"; + +type Node = { + /** unique id */ + id: string; + /** human-readable label */ + label?: string; + /** arbitrary type/category */ + type?: string; + /** metric that determines node size, sorting, and filtering */ + strength?: number; + /** any extra info about edge */ + [key: string]: string | number | undefined; +}; + +type Edge = { + /** unique id */ + id: string; + /** human-readable label */ + label?: string; + /** start node id */ + source: string; + /** end node id */ + target: string; + /** arbitrary type/category */ + type?: string; + /** -1 = backwards, 1 = forwards, 0 = both, undefined = neither */ + direction?: -1 | 0 | 1; + /** metric that determines edge size, sorting, and filtering */ + strength?: number; + /** any extra info about edge */ + [key: string]: string | number | undefined; +}; + +type Props = { + nodes: Node[]; + edges: Edge[]; +}; + +/** settings */ +const minNodeSize = 30; +const maxNodeSize = 50; +const minEdgeSize = 1; +const maxEdgeSize = 3; +const edgeLength = maxNodeSize * 1.5; +const fontSize = 10; +const padding = 10; +const selectedColor = "black"; +const aspectRatio = 16 / 9; +const boundingBox = { + x1: 8 * -minNodeSize * aspectRatio, + y1: 8 * -minNodeSize, + x2: 8 * minNodeSize * aspectRatio, + y2: 8 * minNodeSize, +}; + +/** style accessors, extracted to avoid repetition */ +const getNodeLabel = (node: NodeSingular) => node.data().label; +const getNodeSize = (node: NodeSingular) => node.data().size; +const getNodeColor = (node: NodeSingular) => + node.selected() ? selectedColor : node.data().color; +const getNodeOpacity = (node: NodeSingular) => (node.active() ? 0.1 : 0); +const getEdgeLabel = (edge: EdgeSingular) => + truncate(edge.data().label, { length: 10 }); +const getEdgeSize = (edge: EdgeSingular) => edge.data().size; +const getEdgeArrowSize = () => 1; +const getEdgeColor = (edge: EdgeSingular) => + edge.selected() ? selectedColor : edge.data().color; +const getEdgeArrow = + (directions: Edge["direction"][]) => (edge: EdgeSingular) => + directions.includes(edge.data().direction) ? "triangle" : "none"; +const getEdgeOpacity = (node: NodeSingular) => (node.active() ? 0.1 : 0); + +/** node style options */ +const nodeStyle: Css.Node | Css.Core | Css.Overlay = { + width: getNodeSize, + height: getNodeSize, + backgroundColor: getNodeColor, + label: getNodeLabel, + "font-size": fontSize, + color: "white", + "text-outline-color": "black", + "text-outline-opacity": 1, + "text-outline-width": fontSize / 15, + "text-halign": "center", + "text-valign": "center", + "text-max-width": getNodeSize, + "text-wrap": "wrap", + // @ts-expect-error no type defs + "underlay-padding": minNodeSize / 4, + "underlay-opacity": getNodeOpacity, + "underlay-shape": "ellipse", + "overlay-opacity": 0, +}; + +/** edge style options */ +const edgeStyle: Css.Edge | Css.Core | Css.Overlay = { + width: getEdgeSize, + "curve-style": "bezier", + "control-point-step-size": maxNodeSize, + "line-color": getEdgeColor, + "source-arrow-color": getEdgeColor, + "target-arrow-color": getEdgeColor, + "source-arrow-shape": getEdgeArrow([0, 1]), + "target-arrow-shape": getEdgeArrow([0, -1]), + "arrow-scale": getEdgeArrowSize, + label: getEdgeLabel, + "font-size": fontSize, + color: "white", + "text-outline-color": "black", + "text-outline-opacity": 1, + "text-outline-width": fontSize / 15, + "text-rotation": "autorotate", + // @ts-expect-error no type defs + "underlay-padding": minEdgeSize / 2, + "underlay-opacity": getEdgeOpacity, + "underlay-shape": "ellipse", + "overlay-opacity": 0, + "loop-direction": "0", +}; + +/** import non-built-in layout algorithms */ +cytoscape.use(fcose); +cytoscape.use(dagre); +cytoscape.use(cola); +cytoscape.use(klay); +cytoscape.use(avsdf); +cytoscape.use(spread); + +/** layout algorithms and their options */ +const layouts = [ + { + /** https://js.cytoscape.org/#layouts/random */ + name: "random", + label: "Random", + padding, + boundingBox, + }, + { + /** https://js.cytoscape.org/#layouts/grid */ + name: "grid", + label: "Grid", + padding, + boundingBox, + avoidOverlap: true, + avoidOverlapPadding: padding, + spacingFactor: 1.5, + condense: true, + sort: undefined, + }, + { + /** https://js.cytoscape.org/#layouts/circle */ + name: "circle", + label: "Circle", + padding, + boundingBox, + avoidOverlap: true, + spacingFactor: 1, + radius: (boundingBox.y2 - boundingBox.y1) / 4, + startAngle: (3 / 2) * Math.PI, + clockwise: true, + sort: undefined, + }, + { + /** https://js.cytoscape.org/#layouts/concentric */ + name: "concentric", + label: "Concentric", + padding, + boundingBox, + startAngle: (3 / 2) * Math.PI, + clockwise: true, + minNodeSpacing: minNodeSize, + avoidOverlap: true, + spacingFactor: 1, + }, + /** https://js.cytoscape.org/#layouts/breadthfirst */ + { + name: "breadthfirst", + label: "Breadth First", + padding, + boundingBox, + directed: false, + circle: false, + grid: false, + spacingFactor: 1.5, + avoidOverlap: true, + }, + { + /** https://js.cytoscape.org/#layouts/cose */ + name: "cose", + label: "CoSE", + padding, + boundingBox, + componentSpacing: maxNodeSize, + idealEdgeLength: edgeLength, + }, + { + /** https://github.com/iVis-at-Bilkent/cytoscape.js-fcose?tab=readme-ov-file#api */ + name: "fcose", + label: "fCoSE", + padding, + quality: "default", + animate: false, + nodeSeparation: minNodeSize, + idealEdgeLength: edgeLength, + }, + { + /** https://github.com/cytoscape/cytoscape.js-dagre?tab=readme-ov-file#api */ + name: "dagre", + label: "Dagre", + padding, + boundingBox, + spacingFactor: 1, + }, + { + /** https://github.com/cytoscape/cytoscape.js-cola?tab=readme-ov-file#api */ + name: "cola", + label: "Cola", + padding, + boundingBox, + animate: false, + nodeSpacing: 0, + avoidOverlap: true, + edgeLength: edgeLength, + edgeSymDiffLength: edgeLength, + edgeJaccardLength: edgeLength, + }, + { + /** https://github.com/cytoscape/cytoscape.js-klay?tab=readme-ov-file#api */ + name: "klay", + label: "Klay", + padding, + klay: { + aspectRatio: 16 / 9, + borderSpacing: padding, + compactComponents: false, + edgeSpacingFactor: 0.5, + inLayerSpacingFactor: 0.5, + spacing: minNodeSize, + thoroughness: 7, + }, + }, + { + /** https://github.com/iVis-at-Bilkent/cytoscape.js-avsdf?tab=readme-ov-file#api */ + name: "avsdf", + label: "AVSDF", + padding, + animate: false, + nodeSeparation: edgeLength, + }, + { + /** https://github.com/cytoscape/cytoscape.js-spread?tab=readme-ov-file#api */ + name: "spread", + label: "Spread", + padding, + boundingBox, + minDist: edgeLength, + }, +] as const; + +/** layout algorithm dropdown options */ +const layoutOptions = layouts.map(({ name, label }) => ({ + id: name, + primary: label, +})) satisfies Option[]; + +const Network = ({ nodes: _nodes, edges: _edges }: Props) => { + const container = useRef(null); + const graph = useRef(null); + const layout = useRef(null); + + /** selected nodes/edges */ + const [selectedItems, setSelectedItems] = useState<(Node | Edge)[]>([]); + + /** max number of nodes */ + const [maxNodes, setMaxNodes] = useState(20); + + /** selected node/edge layout algorithm */ + const [selectedLayout, setSelectedLayout] = useState< + (typeof layoutOptions)[number]["id"] + >(layoutOptions[6]!.id); + + /** selected layout parameters */ + const layoutParams = + layouts.find((layout) => layout.name === selectedLayout) ?? layouts[0]!; + + /** full width */ + const [expanded, setExpanded] = useLocalStorage("network-expanded", false); + + /** map of node types to colors */ + const nodeColors = useMemo( + () => getColorMap(_nodes.map((node) => node.type ?? "")), + [_nodes], + ); + /** range of node strengths */ + const [minNodeStrength = 0, maxNodeStrength = 1] = useMemo( + () => extent(_nodes.flatMap((node) => node.strength ?? [])), + [_nodes], + ); + /** derive and map properties for each node */ + const nodes = useMemo( + () => + orderBy(_nodes, "strength") + .slice(0, maxNodes) + .map((node) => ({ + ...node, + label: node.label ?? node.id, + type: node.type ?? "", + size: lerp( + node.strength ?? minNodeStrength, + minNodeStrength, + maxNodeStrength, + minNodeSize, + maxNodeSize, + ), + color: nodeColors[node.type ?? ""]!, + })), + [_nodes, maxNodes, minNodeStrength, maxNodeStrength, nodeColors], + ); + + type Node = (typeof nodes)[number]; + type Edge = (typeof edges)[number]; + + /** map of edge types to colors */ + const edgeColors = useMemo( + () => getColorMap(_edges.map((edge) => edge.type ?? "")), + [_edges], + ); + /** range of edge strengths */ + const [minEdgeStrength = 0, maxEdgeStrength = 1] = useMemo( + () => extent(_edges.flatMap((edge) => edge.strength ?? [])), + [_edges], + ); + /** derive and map properties for each edge */ + const edges = useMemo( + () => + _edges + .map((edge) => ({ + ...edge, + label: edge.label ?? edge.id, + type: edge.type ?? "", + size: lerp( + edge.strength ?? minEdgeStrength, + minEdgeStrength, + maxEdgeStrength, + minEdgeSize, + maxEdgeSize, + ), + color: edgeColors[edge.type ?? ""]!, + })) + /** remove edges whose source/target nodes have been filtered out */ + .filter( + (edge) => + nodes.find((node) => node.id === edge.source) && + nodes.find((node) => node.id === edge.target), + ), + [_edges, nodes, minEdgeStrength, maxEdgeStrength, edgeColors], + ); + + useEffect(() => { + if (!container.current) return; + if (graph.current) return; + + /** init graph */ + graph.current = cytoscape({ + container: container.current, + minZoom: 0.2, + maxZoom: 5, + style: [ + { selector: "node", style: nodeStyle }, + { selector: "edge", style: edgeStyle }, + ], + }); + + /** attach listeners */ + graph.current.on("dblclick", () => graph.current?.fit(undefined, padding)); + graph.current.on("select unselect", "node, edge", (event) => + setSelectedItems( + event.cy + .elements(":selected") + .map((element) => element.data() as Node | Edge), + ), + ); + }, [nodes, edges]); + + useEffect(() => { + if (!graph.current) return; + + /** quick lookups for existing (old) and incoming (new) nodes/edges */ + const oldNodes = Object.fromEntries( + graph.current.nodes().map((node) => [node.id, true]), + ); + const oldEdges = Object.fromEntries( + graph.current.edges().map((edge) => [edge.id, true]), + ); + const newNodes = Object.fromEntries(nodes.map((node) => [node.id, true])); + const newEdges = Object.fromEntries(edges.map((edge) => [edge.id, true])); + + /** remove nodes/edges that no longer exist */ + graph.current.remove( + graph.current.nodes().filter((oldNode) => !newNodes[oldNode.id()]), + ); + graph.current.remove( + graph.current.edges().filter((oldEdge) => !newEdges[oldEdge.id()]), + ); + /** add new nodes/edges */ + graph.current.add( + nodes + .filter((newNode) => !oldNodes[newNode.id]) + .map((newNode) => ({ group: "nodes", data: newNode })), + ); + graph.current.add( + edges + .filter((newEdge) => !oldEdges[newEdge.id]) + .map((newEdge) => ({ group: "edges", data: newEdge })), + ); + + /** update layout */ + layout.current?.stop(); + layout.current = graph.current.layout(layoutParams); + layout.current.start(); + }, [nodes, edges, layoutParams]); + + /** on resize */ + useResizeObserver(container, () => { + graph.current?.resize(); + graph.current?.fit(undefined, padding); + }); + + /** download network */ + const download = useCallback( + (format: string) => { + if (!graph.current) return; + + if (format === "png") + graph.current + .png({ output: "blob-promise", scale: 2 }) + .then((blob) => downloadPng(blob, "network")); + + if (format === "jpg") + graph.current + .jpeg({ output: "blob-promise", scale: 2 }) + .then((blob) => downloadJpg(blob, "network")); + + if (format === "csv" || format === "tsv") { + const download = format === "csv" ? downloadCsv : downloadTsv; + download( + nodes.map((node) => omit(node, ["color", "size", "strength"])), + ["network", "nodes"], + ); + download( + edges.map((edge) => omit(edge, ["color", "size", "strength"])), + ["network", "edges"], + ); + } + + if (format === "json") downloadJson(graph.current.json(), "network"); + }, + [nodes, edges], + ); + + /** fullscreen viz */ + const [, { toggleFullscreen }] = useFullscreen(container); + + return ( + +

+ {/* legend */} + + {selectedItems.length ? ( + /** show info about selected nodes/edges */ + <> + + Selected items + {selectedItems.map((node, index) => ( + +
+
+ Name + {node.label} + {Object.entries( + omit(node, [ + "id", + "source", + "target", + "label", + "direction", + "strength", + "size", + "color", + ]), + ).map(([key, value]) => ( + + {startCase(key)} + {value} + + ))} +
+
+ ))} +
+ + ) : ( + /** if nothing selected, show color key */ + <> + +
+ Nodes{" "} + + {formatNumber(nodes.length)} + +
+ + {Object.entries(nodeColors).map(([key, value]) => ( + +
+
+ {startCase(key) || "none"} +
+ + ))} + + + +
+ Edges{" "} + + {formatNumber(edges.length)} + +
+ + {Object.entries(edgeColors).map(([key, value]) => ( + +
+
+ {startCase(key) || "none"} +
+ + ))} + + + )} + + + {/* cytoscape mount container */} +
+
+ + {/* controls */} + + + + + + + + +
{/* controls */} - + {/* pagination */}