From 320a4c4e236337c70914180b625a58f6c5dd43f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 26 Feb 2019 16:47:56 -0500 Subject: [PATCH 1/7] sankey: add attributes node.(x|y) and update them on drag --- src/traces/sankey/attributes.js | 12 ++ src/traces/sankey/defaults.js | 2 + src/traces/sankey/render.js | 76 ++++++++-- test/image/baselines/sankey_x_y.png | Bin 0 -> 34933 bytes test/image/mocks/sankey_x_y.json | 29 ++++ test/jasmine/tests/sankey_test.js | 227 ++++++++++++++++------------ 6 files changed, 241 insertions(+), 105 deletions(-) create mode 100644 test/image/baselines/sankey_x_y.png create mode 100644 test/image/mocks/sankey_x_y.json diff --git a/src/traces/sankey/attributes.js b/src/traces/sankey/attributes.js index 9e0929100c7..9c78e859c63 100644 --- a/src/traces/sankey/attributes.js +++ b/src/traces/sankey/attributes.js @@ -102,6 +102,18 @@ var attrs = module.exports = overrideAll({ 'Multiple groups can be specified.' ].join(' ') }, + x: { + valType: 'data_array', + dflt: [], + role: 'info', + description: 'The normalized horizontal position of the node.' + }, + y: { + valType: 'data_array', + dflt: [], + role: 'info', + description: 'The normalized vertical position of the node.' + }, color: { valType: 'color', role: 'style', diff --git a/src/traces/sankey/defaults.js b/src/traces/sankey/defaults.js index 531dfffcf50..fa31a1596c6 100644 --- a/src/traces/sankey/defaults.js +++ b/src/traces/sankey/defaults.js @@ -33,6 +33,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } coerceNode('label'); coerceNode('groups'); + coerceNode('x'); + coerceNode('y'); coerceNode('pad'); coerceNode('thickness'); coerceNode('line.color'); diff --git a/src/traces/sankey/render.js b/src/traces/sankey/render.js index ae392c101cf..09cc72b0965 100644 --- a/src/traces/sankey/render.js +++ b/src/traces/sankey/render.js @@ -23,6 +23,8 @@ var repeat = gup.repeat; var unwrap = gup.unwrap; var interpolateNumber = require('d3-interpolate').interpolateNumber; +var Plotly = require('../../plot_api/plot_api'); + // view models function sankeyModel(layout, d, traceIndex) { @@ -163,6 +165,25 @@ function sankeyModel(layout, d, traceIndex) { } computeLinkConcentrations(); + // Force node position + if(trace.node.x.length !== 0 && trace.node.y.length !== 0) { + var i; + for(i = 0; i < Math.min(trace.node.x.length, trace.node.y.length, graph.nodes.length); i++) { + if(trace.node.x[i] && trace.node.y[i]) { + var pos = [trace.node.x[i] * width, trace.node.y[i] * height]; + graph.nodes[i].x0 = pos[0] - nodeThickness / 2; + graph.nodes[i].x1 = pos[0] + nodeThickness / 2; + + var nodeHeight = graph.nodes[i].y1 - graph.nodes[i].y0; + graph.nodes[i].y0 = pos[1] - nodeHeight / 2; + graph.nodes[i].y1 = pos[1] + nodeHeight / 2; + } + } + // Update links + sankey.update(graph); + } + + return { circular: circular, key: traceIndex, @@ -399,6 +420,7 @@ function nodeModel(d, n) { partOfGroup: n.partOfGroup || false, group: n.group, traceId: d.key, + trace: d.trace, node: n, nodePad: d.nodePad, nodeLineColor: d.nodeLineColor, @@ -425,7 +447,8 @@ function nodeModel(d, n) { graph: d.graph, arrangement: d.arrangement, uniqueNodeLabelPathId: [d.guid, d.key, key].join('_'), - interactionState: d.interactionState + interactionState: d.interactionState, + figure: d }; } @@ -509,7 +532,7 @@ function attachPointerEvents(selection, sankey, eventSet) { }); } -function attachDragHandler(sankeyNode, sankeyLink, callbacks) { +function attachDragHandler(sankeyNode, sankeyLink, callbacks, gd) { var dragBehavior = d3.behavior.drag() .origin(function(d) { return { @@ -520,6 +543,9 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) { .on('dragstart', function(d) { if(d.arrangement === 'fixed') return; + Lib.ensureSingle(gd._fullLayout._infolayer, 'g', 'dragcover', function(s) { + gd._fullLayout._dragCover = s; + }); Lib.raiseToTop(this); d.interactionState.dragInProgress = d.node; @@ -533,9 +559,9 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) { if(d.forceLayouts[forceKey]) { d.forceLayouts[forceKey].alpha(1); } else { // make a forceLayout if needed - attachForce(sankeyNode, forceKey, d); + attachForce(sankeyNode, forceKey, d, gd); } - startForce(sankeyNode, sankeyLink, d, forceKey); + startForce(sankeyNode, sankeyLink, d, forceKey, gd); } }) @@ -553,8 +579,9 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) { d.node.x0 = x - d.visibleWidth / 2; d.node.x1 = x + d.visibleWidth / 2; } - d.node.y0 = Math.max(0, Math.min(d.size - d.visibleHeight, y)); - d.node.y1 = d.node.y0 + d.visibleHeight; + y = Math.max(0, Math.min(d.size - d.visibleHeight / 2, y)); + d.node.y0 = y - d.visibleHeight / 2; + d.node.y1 = y + d.visibleHeight / 2; } saveCurrentDragPosition(d.node); @@ -570,6 +597,7 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) { d.node.childrenNodes[i].x = d.node.x; d.node.childrenNodes[i].y = d.node.y; } + if(d.arrangement !== 'snap') persistFinalNodePositions(d, gd); }); sankeyNode @@ -577,7 +605,7 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) { .call(dragBehavior); } -function attachForce(sankeyNode, forceKey, d) { +function attachForce(sankeyNode, forceKey, d, gd) { // Attach force to nodes in the same column (same x coordinate) switchToForceFormat(d.graph.nodes); var nodes = d.graph.nodes @@ -590,11 +618,11 @@ function attachForce(sankeyNode, forceKey, d) { .radius(function(n) {return n.dy / 2 + d.nodePad / 2;}) .strength(1) .iterations(c.forceIterations)) - .force('constrain', snappingForce(sankeyNode, forceKey, nodes, d)) + .force('constrain', snappingForce(sankeyNode, forceKey, nodes, d, gd)) .stop(); } -function startForce(sankeyNode, sankeyLink, d, forceKey) { +function startForce(sankeyNode, sankeyLink, d, forceKey, gd) { window.requestAnimationFrame(function faster() { var i; for(i = 0; i < c.forceTicksPerFrame; i++) { @@ -609,6 +637,14 @@ function startForce(sankeyNode, sankeyLink, d, forceKey) { if(d.forceLayouts[forceKey].alpha() > 0) { window.requestAnimationFrame(faster); + } else { + // Make sure the final x position is equal to its original value + // necessary because the force simulation will have numerical error + var x = d.node.originalX; + d.node.x0 = x - d.visibleWidth / 2; + d.node.x1 = x + d.visibleWidth / 2; + + persistFinalNodePositions(d, gd); } }); } @@ -628,13 +664,31 @@ function snappingForce(sankeyNode, forceKey, nodes, d) { maxVelocity = Math.max(maxVelocity, Math.abs(n.vx), Math.abs(n.vy)); } if(!d.interactionState.dragInProgress && maxVelocity < 0.1 && d.forceLayouts[forceKey].alpha() > 0) { - d.forceLayouts[forceKey].alpha(0); + d.forceLayouts[forceKey].alpha(0); // This will stop the animation loop } }; } // basic data utilities +function persistFinalNodePositions(d, gd) { + var x = []; + var y = []; + for(var i = 0; i < d.graph.nodes.length; i++) { + var nodeX = (d.graph.nodes[i].x0 + d.graph.nodes[i].x1) / 2; + var nodeY = (d.graph.nodes[i].y0 + d.graph.nodes[i].y1) / 2; + x.push(nodeX / d.figure.width); + y.push(nodeY / d.figure.height); + } + Plotly.restyle(gd, { + 'node.x': [x], + 'node.y': [y] + }, d.trace.index) + .then(function() { + if(gd._fullLayout._dragCover) gd._fullLayout._dragCover.remove(); + }); +} + function persistOriginalPlace(nodes) { var distinctLayerPositions = []; var i; @@ -795,7 +849,7 @@ module.exports = function(gd, svg, calcData, layout, callbacks) { sankeyNode .call(attachPointerEvents, sankey, callbacks.nodeEvents) - .call(attachDragHandler, sankeyLink, callbacks); // has to be here as it binds sankeyLink + .call(attachDragHandler, sankeyLink, callbacks, gd); // has to be here as it binds sankeyLink sankeyNode.transition() .ease(c.ease).duration(c.duration) diff --git a/test/image/baselines/sankey_x_y.png b/test/image/baselines/sankey_x_y.png new file mode 100644 index 0000000000000000000000000000000000000000..3676157c6f99c2230cf4c5a99b59f5f36ab06b21 GIT binary patch literal 34933 zcmeFZWmuGJ+crEi3^>9Fy92 zx}-tiJuh7MbFce-w(b4#{rG;}Yugmp%ype{oJa1*e)3#HO^KA4ju-}mk={_2*Mh+a zFwh?q5%^^EA(1rpJuAchFeOTNwP(c_CWa ze+>hDi9q@M^M8^59q7L@`v0m&{%wSXRd~A1k7T}Gx75|+j2x=I#6S9a0k7oyQ>pEB z;?|{7hjFpFa%rcZ!5%}OnC>4>o*eq&;bf0-o12H%r-P)nST@y9w$$-GH+fCAwkjMN z4r?0he5YxBKPqWPO3sm(t|Oc)YsTAa=M{K!7uQ1_y+5_wsIW@(xX-_R5O%Uhwi)EN z7o=-;eJQ_`=IU;WTu@ul$w5)voaCAZ-pUtmm6|^}dAu?i>t45Hf~QQ{lc$>XYO`dr zOx*j-7TYYj`baY>NEU;`dLfYEJIhu4hzY{_?jhhxu-$g`J2XTAIc$ zzkxZ8yAHl{Z#W#si}-Ddw70Y<>rOlPOwV_x;Zs9+avd_)9))>bKVhi4&I11&cySKc zC^G_yX3mYL^jAh6PmvurE{~Mw9nc8&MOeVzIi|&Z-|Uobp!>#x6I>FlLWXDhq`Xt9 zDY}R&Tk7m5$C}>(HeN^I|B{fW>LW^bgJD^JcRTP@+pX=t9Uf?burVgH&eBL%~dXM!J^1##ZI05kW^&MJn>gl z)?4Tdgvq?82*OTQhe|B_E4ztFJH7D&VJGhWN6?AoIp*o=d0FRn?d^ToubVuq%VjV- zTGZ!a>tCAX_4ClJ@0fYPRe0^wr%(O+y0o`_7H*p5wo38w-AO#?%iqp&3Pdl9qrlY3t& z7x#fAckz(swoCv1B+YQLrh3q90heDXS}<69+$^4JW0=q!H?egk%|@N>-dn$+17qL) zS-e2f{>%NbIVr1iOcK@uMP_E9Q{M_VKP=|vak7@DJP>i5Fs@T8stoeYB7f!RcQ~rp zn=L50oVZ(?A;qyf}1xbZB?j^n2m4YHQb4X*xgGUnjF>#~J(0e!rDU-PcS@ z@3+V1no0a*ArnnmJz%I?^I6MVEbMHL;WoE(_@txZBwhb{XW|&CGn`LHEEjq*@L9f# zS-DaLov|3RM&IJVC?0D8JxBR@}S_*yF?DseBy4~&V<%6bnz6V);Bk{fm z3;pe{97bGUdlq?(=Xr)XP#ohOk29<2CJerkk;gMCyaxe9}1YIq^w8 zUf+$7ys6avUt%uW`Ef5(Qc`B7XV;Eb*K*_Tk5A2i#g`@><#)F)m>u<-B@{oy&i1Hg zI^=I1cg(mJd6~EpoZv+o_V@gJ2TBv~onyGJ5lB!YB_pC-P3{R)$Z)*Hl_-4Fv0mX5KAKBkPh^5K z>B)-`Rgo;tq`C`hmvMp%%prv6=#ARV4uReJ-8%KR_{?8Y#~Wcq4(8&YVhGE&HGP=3 zvz2u1A1@VD>iNjZ+z=_VO7d~rb}IikI(fX4+rH{Gd8jun%L|>MO z|JL7LHNcvEl1;WWcUJwetZQNij9*w-Z0QQ*00+=cKvFLWVEJ_@AR+ch8^S; z+6@{U0heb*o!s}P-|XJjg1TSz!R-TokNt@-byfpQ)VKd6hY zPpk>8Xn&MVazBzx@hz;-!u7nN7RJpt64Q zcSnkKNZ?$_j_$|kd?nG!Wwej`E z%&JkUtK$!CJmVy(44Dpxn)O5)NVtp3MJlQ_dp#Mv=J5t@Yknt(>ISb_2?7b}W2?94 z)qQ`x?r$$Y?yKA#IF=#v)DV*G^O#pOyVsYr^R;3D)$-fUeTmssyI9cn%38-%b~hpv zm19!Pty8((UAep{op7YRlG~N(x%gFgyNC3HL!`J_mfv9e`;nVzUi@9zu@0U!jN>$& zyaTikU^a$CCfY{@->h!1Dm84-H)sZicBqm~tUnXcVI^|AtDW|kIGwI)QNr|BP*zPlT#}T__Us~@`DH7VSZPcJUes_U>QNM0cm+e+}m&jrD zg}!#@xfDmQa+Ja3-K8$oPzTh?7K4{_+N2%w&T)#3HZd37*>0UzP}smHo$M#&#jyll zFi|IYw!M<;5KPtfV0@YCs#nJs2QgRe(F?b>m;xCH(>mydd>j zCfk!@WZRU4Jb~2bkmwd|94ykRMvDtcL$%jLTW?+_EYEs&XY`?}PtW{ENCEUjE*uDJ zZBDvc-f@4nD6EnkG6egz5$0EGNNM3n=W3Z1r#6mWykEI_Up|eNm~t~q!=}C_6UXsk zUgNx`=t^kDlb`|Cp{cHd$Z8wSy~iESiQ0h(Rk?>M$KRk){eHP%#E8YjqM z#tTosagt4f$$gp`o})g0MbD3u%Jz~SF)#LkKQ zr7On0K`x*2J8{QHMw15v8}$~I>8m2REv;vnNtXk;LK$+#5`q-R?xvX+3bD}T^eLw~ ztc#Nkx1{sn+Q=ryr3Me$Y(0|?27adrT(j2E#_tG>?Hd+w^io|XL~olM9i{Th;Qh!` zyVZRhYD_{JM#!-kGNAfQE6UvH`5TSLVJ57EJM=ew(hBHhYPPfMIZnEnkGks#T8e(R z7U@j3F4k6WXoMFB6&BI67kqr{XPx^tAtqz8ZuuT>UgnhrqMMV&i$wR2=jG-^1sQJ{ zE!svYXlF00<~Rf|S5u6`>Oc-ET^}sE1G~uO^C|yJE%9|IgUy4b(kl^Czk$oADOOii zhIGa4gk6E(T^cCpM+FzAOyz#*ul{IieW`;8t-8=%Ih)WtI+U!guoUO*GgIVvb7l253AMqbUfzRP+w^YOcrniolWI|S<#hF(K`)^|g&=&a-A29x z&V?Y~F=!>!v65c4-%Ld5z-^Ez#D{gw87be3cRB7*h3i}yH_zocLZYjzJx6{s_VhXM zm%-C*POK@gI`;`RReiJ`_t3G3U(Po=G;-vYT8n>?8Npvb*%cf!&}S`=UD93*ja7ChTZi&oss8t$QDT^V?+!_)K?I5y|&fQ`weL(vx)TC($78fs($}*3s7J zEzTjgOhdm{OHoNF&xUuTL#J0^OWJ1~HsbMvnALDApWD<`J8waqj{JfkF4$OvjrgKr zZwd!1@$~~eqcldkQ}0UNO?7R2%vyxUYyQ5r$QxqD1H9SfXCAkC#apbGvJ%`3g)S?k zVRkh3x=6EVzPs^UTyvAlj$^%T{USWGacn`&Jy$KwS@_)#!G@PlZ+(n>Q*Sa*#z)9C zzP#vN&7pJQ@GOQ-vWqlV@vZL$tzU`ZRuTEd+w;GLae^z%Kp+EXo9^0!H;Q@7eOX@+ z!B0|Up9~OMbNed__63Q%f7CkZsoWe{QT104FR*dX;`Q7A-p_N#*15cH!Yk=;EGt$m zT`Vtggj?l~T8*izT^1;DZd}mm%!JDq^|Q|k4H|3rFRi*aczsC^dcIitKndaMT;$g0 zNgR5mMR`=rZuhF{xHCCc@SaN2Z9+nySAr@z358`7axxl8lwQ^)Rc;NvhL?QqmkSju z_Uq7u`VNt!x!(_>a)cARtif6S_X7$;Om4VU7+Ha?&4!io8lnZG2llgu?(CR(MJ{?`Ta_-{@EUAbd{4gDSU1@{IiCSG#~)y(v@<+YD$z z;cYI)#ZFu8ZToHMK%FDCS3COx;|OJ5pOnJC>cBuUwfUUN;}J{>}0 zDd-|s9YfmG#@uc!EnI$9Z*)N*eZl!xFV(d`{c@{0L9qhnUhC(_YMNdg*KJQsfM&qf@LPrbXzuu{ z<#Q%B1KDF*c8jm@J9f5FnOGZQ^Gdd)PFlkU-F{WEeISKJE1Q9s>r1aifP7EY75fBa zCMP-g`K6?CY$B^Fyx>CDGZfuM*G}8W`|@vvO2}}*(KszufbqEkj6saWH|*bN9}Lj} zR^P!cO8!3pAo#KmXxn1Ip5#A2yH1J0T~GTd|1X>n8bgB(3(KMWvHQ=@rf%V2KVC*s zviuWMOn|}D)q*FXHT>&YoB$jVETEzm@b8cQyTDWV{8xgI82+n+z*7EeB>#0Y|MHpt zzdP#d)Yk!Q-rf1}>6tufGDb0%-@gxc*W5k1)t+JL5ou{@&Aj3g5)Wc{Ol-F%pFMk) z%X|*0LW{vgO%P;D13y1?d#Ym8LEy|ehHJ#MrKP31^HbPl9XKI6FarEHlaj7OvS?BW zBEwik79LV|egCQ|Q2-%MaJoDMh`76dNeN%6Z9iw4w2)SlKlUt$UKED!#4ZAf_#*{J z_yh#BkLHs^(b(j7xTYZ4mRKRXx0(GfU+Q1JiiO?yu)!_{R)G$IePoS^NoEj5<(t;U zVj|__w1Nl;Q5cs$(Hk16#Hy1?QG!V8XFDqXYI1k@#>S(}``x1NLCCv1n685YAs5A( ztT0>Sz9w~tHt(7~o)Z-XK3Jlpr0n0idJgGCj=}xxiR>eQo@7rZCp&^Ra2~GuT?>6D z#q+#l;SYTf76;#yLLu<>S?hx4tu2@G3?2Yq22ghiC=3)BS#7bML2!_uv04t2t*?OI zBdNLw2(mIW4VK8e;%~BCGsCiC03H#HI)lg|29Mc`wub(BEI3F}YRFIoBlbqtoZP?M zZjeHDp5+^OCemJv7#b>oyz3LvePx6iZ(&RbN79j?W$Cmxnp!~Yj}StAkpT@k3yLo7 zHlbf1{b7hl#Aw+c4`@}&KfvGtOiv&a?9#;vF}wVQ>_KA!(!157ew~9b{?mrw)FpV+ znFIvLn}i+4tU<-l%PE+_t%rkYJ?Pbb1Tv)L8XQfmT_sl_8c1AO4qIbsKT-Q`4vb^` z{RCOd=R1LCr9U~Gl5Dj=@DJp0j#jr=ei-k2ElFye&mf7mQYnTuxqBiBghZtTk| z8x8AWH79)gLc#1mroiD6UFtzo6sG@!0;rBGuE0Z%-8s}3sLN67#_1|p;elkQJ86+K zx3miD8>mCojcd7=fOxBs1EVZS1ly`vw^ zXbB--@+}7|hy-f_@mS_*T!F+B-UyV)V^s-b$MY0xgfM(rxM0A5`g{=!d!BKb9Vi+G zZGg*4JVP|+getpq4RkKPHa8npi@i~px(gj9)bC~2F`w&5xbNUl*fpYUk8CAB&%`9` zDJFIXQGx<1wK>}g)B}a2F+b2Xf5;Pr4M7-HJm9J2@c{1Q>u>J^bZS4Xzvgv7SLbvLs zynK$^;%>{3#w8>+c5W9c=rSB{5eTYV{1LJ7%%oCof z!ruVRH8DbP9vW}4aoRFvQ^qsV`G)`E0!TBUqByUKi`!*pWf|p2X)_z$nM*YRf^{K* z1bfY2m>`(xS!RKoD4JatOVYjlSEBH_dYH9bf}I=iS6#eNNz#NI`3rOP33q;}=6E5( z-X0+XOOgBfPgPLH8a-JZDd+0yU;XgcD2|A2?nwj?&qe$5FQ`EVPw_IAgaz#W5o-RU z^}$&mG*&jmxz2!ak3C7ky z?@Rt)?<2#pUB*QIgh((y1$I92KeUm;dI=vy9*!k#yO&C9y(FLvdtPrxrHw@>^Qo#T zkb?!dW%+@V_eYf-UbuwoBCrJB`g0FO)Ty_=)HP691jhe=yxrh)8N5Yqy$gm;IrE=l zOfGOVJB#tBd;49Uw`;&E97f?Y$exKqhFiqOzJMatitnKcc^C?4<$_?#-JT zQ1HQoV~r;3DX{+!!HgX3M1!~FlgEM70+`N+073;6%pfJk_*c|}l_71-c_`~n<*vMC zlJ*usfqh}KxK1qzOy*6xl^(-ddW!>L@;oyeRNS7q(JxR7Xyu7!?cv;ymIef zg{K>BFDpN<@7Bj^V!Hiq&RPrL9!%6$5kCUP266?Cs_mQ_$afJq0^4*fQ;5HghxHV| zspRzv3|%F{KaVNf6NNv2epWzAib;U%o?K%M4E|Z1k2csCNY8^H!&m~L+*&Z`JFHED zNw7G0=J()1es%Y&jZt6j^~nl*Bd*Y^WT8(UVl>`^bm!+l*6K^Zl&J;wR{QI|->)bF zWvdx$&zL8{h#R+h%K)PxcHjVQ6KgT%>gz@7kRgi#$Rs>Vd z(O|{sRhJZ2o!->wEGdkLWoBl!`4gNOsrDA4OW(%i=@&T^{AGOJzXC|a{+si-0r*)` zCjV>}WJ5#Q@VqEzSjkI!qKFCy9NwLUFx)^f5aH&?qYK%-bfz@T#Ky&3Dfp$-uPp`a zEQl7+cx!uq?|ZJJcG$v=z^`wO9P9eWd=FQu9?|G0X~2I*X{+TNgdg{%)H`p@cPIN~ zhhlp0Y6hF)rBV%Uv?Wj*cYI(k#Ox?_v$K(4br+V_Aa@;&;>NxKaa_FTVorakKNHa|z?Z)m1K8`nr{~DnvdGSj;V5#$X(`ntbb8pOitM-?z zr0vQCdY8;j4mdwoRAG%&vyKb8(MjS89M%g0ubaqk0@6o@+k3Ns&lUYn#4Coj7tUfb zDkQcmfUz7r5%EXE$?k5qb#wXR!`Vm4R$Oc(V7BB(rd zz3yGYm05L0&j60u=k9a2W17HmH{J0$eEju-)mx~uJxQ~4Uu)8o65F$;B}EW?G+?+S z&2%)c6em)@5!%01*XgtBtP6W?PnV=-Pe@W?Gkvx}=VG-4WID<01t8%vyu&+_T2BzHTictkszZn#e@l-2 zwO~{aIw6#Tw!#7emWe07=k!zLdP@pF{CKgL1R7D5dUnBr#64ft3|zVAXW+i}($sZ@ z9S1(G@XrJ4A2ns}&(`rTc!25u_=Us2FvR)L3AKY}EZ){WP_c(fxGZ|} zYbbrYmDJ`tTPoEPrln~odu8H~Zf6uGK}KGC@jUtS?cUi2VKwh2E^~K@y zBhN%CgfJvR)tOt?l+R?&`8brJr3YA>VHO2K6sbF#M`LdcMk_u$kz-XJxGs)yfMihm zF(rx`Th^#e7TN1^VcY<0i7my;Zor&vhZ>95ZGiV*Idn*L!@8kSGPhTpgvh05XjxcQnso8?@ii;f z&%Wv}hn?+(sU|gzyP;Tc@k7jg4s@i`%22obevPKB zJMf+EG4VEuI7Cao+3zTUHQ2#~0mGVI%cMQ&D%~e&Y*lDFSXCMR? zY5QH%K_9%X9~plJj(kBr)ZpjmO5kwPX8&1UMm~+Dx@txEV(P;RuU&^Uo7*Yp0N1c* zWc*eq@wBoM*rJ+|rj|5#UAi!y8jd8U(13MsT_TJGl)neI4q5jBK_RM5T2}4bFGj4I z=xmg2Ytmwl-#|*TDn*P&~aRq&oM~)>R7~hh1OS*!JgA@?^xhjNfW+YkSQ7q!af3?l+23?|ml$etrwRe1k_44>-KX zoW67G7E5^V6^ptpza11i=jkNdlKf$ z)jmEhFCu)1OjdcBd+X^ha%_(^o5})6yMJ`X2tY7UGocHmonzO^E2Esvp=+tw)NTUC z+$@Je<1^$S;ILvqiHfgYDil!nXw}ZOF$nV-n`x6{`i0&EzHE4Tw37GTcsGh=!|y!` zvr$QYc?*=R8%y7Zp@N4JNhRgtTzDcBdT!=1HUQS!;9Gjf+LG8;(XBG5=QSB9%%e)Oj7=c(4aYWWsE#W{01`>4 zvYJeKX{vCzHN0{$TZsBdO=0r_wv0$#nppFoTa1=4CrkQh0Z$(GIKTtqkr3wejodKe zTnb{90>q@|f<>w9^3b02rEw_6BVJv;rEehg}QLUE;i@S0%!+$35dRPhl z>h|#7jGGjkOK>-(mRfbItVvi0JdyG+MfoyIy@uXzBi13I^dfq!;tY>KIR9aNMx{Fu zxK~N9!j}L?xyqg#XCi4o_64!Z@Kz2P4lyqAH`F~NtA4!Tdg(9}$3S;M6Rqj>WlS#? zFDxo*BUY*Ape1cO11#)Eb98AY*zJw10(K6tTlTJjP?ZOLEQsKW(WCVsY2yOIlAuJg z)bQ?~UGu>{t2bZX2|)LxQ-7+>Ea()Xrwp($E7SsKn}761PbU(V zG0BSv7&1q0Mp(i$y@tC!-p zB+j6y!r_*yk8JRgY0dinh^^HCF2K&RuNSV}3*!D4&8$skd(+nEqwmy!-k&9xJ>+g%-3B7-fmAScpg@X$b&*N$MXFW5xyq%XUQvmv8#NLPJ-ILh{`zl!gk}3s|(lmsY@eZq^uE_@C>~a9wiutLYnmo)VFd;OoLgJ$yLV@+r#w=C8>Dd@5teNk8Zw}kEmKZN69FSSn%0U*gP*?k zDxpA8;aLbn)>L|9(C8Swj>2p&+6RPBPv3#AHKa+4#scGtHQH#E0O^oN1y?x)K-Z$$ zIyz!OCnZf<&SCl{%w{0oJ^&qxv%VzHdXb?Wq)+Yfk9F%ow&Fb+6AOWSri8N3$Pvn$ znSa$O6mbs&0{pwTZ>>$co|xT$aeUaw@dS2|!U}<*sK2fzZ(k(7WZ1ZBNy`lutJUI$)==?)>AMELuy^$@1$Cq76#-?6Nl_q1Bl6c6@g4>#ivJXV7e(Tx1B6zW> z`=4oi^6@F21?M(j`->H1&-nup^GY)JN}j@c%ve{(s!9z@d%yCDi5aSXv+zIoc3>YU z$0R@WmCgU(kS83ON}+LH)N^b8>^;wu<_X7F6K+5J=*zlzJ@FxJ0D~ZsRP9^S{}2}& zn@jpwitCP&+)&}}xqIc$!*X7@t|*`K7*c?6Qqrr=0c8Q34+hYXBU7ro3$|rsU;4b2w0>nm!=}n7jT>a z!l#neVGJZ&iOC=*4~!fm3-twPPhC0s!=o9+w08J)w6zh?G&homK7Zao5SML~ZC&Z_ z4c&NWm3Z4nE@>&dCA9&~&b&F&`6a#jk(8Oq~cW(2jFVjc>p`k~Q5-_J z;-kXeFch0J@JEl3-)v4_Mz_ddDzY+a__>8}^BkLW@M$dO3NZa!1v0n)JP?e;yxjWwh%ojC?@B3Sk{0YH@8OyELt(vkh;H$39%*^Bno}ImF08oL4Gup0y z#v>ws3u+@e((bo-|Bt}MC!n&bu ztbt47W)^2>HNa~g=-Wae6$pb1q4bI&C-d%?3SCDBxO|G`c(rRln4YI7Eu#(&#-EK@ zQ#v(AnEz{JnQBqyy)W;vl3B2(GA^lyIpEZ_9A#SnaJR7O2)+W+Zrlrob-DDo6e(PM$XoguMxWj?sRl+1cDhbUagZs0t?3c_E5 z!LkO@dHxJ`=~8k8nvk5^VAm&|2CJy3m|M<7cQl2?Kyl51TLpBHp#3S|!iMy(WCs^5 z+y>|yXy(O1{dS|v13?@Pz+Z$<)B;H8&xntyx}4>y6t`xax;@(fbZa;~^)Wc8x#LSt z8ib4YQc)pKg%3xH5eo9B1}+g_Ja7*0ju=7&%LZlM=6L)vG_-piMFj#FM{ZTl)jz}9 zkPKl`BO;%kA)`CWNvcj;fu%-}N@5?nxRf1qOFLsUazT#X7n3#d_qI5bBO>Pma%)ANGw-+)unNK9~PhKi|kI@gR=ZXx*Eb;Q+X(3wJ)f6Ju)yeV)jhH?_$A zoCXC|@sb1;MldxcP2d)Tq^C=Aj(*JNZB)zT*op)G@*Cs$`$suy50oQ&+q27mpC7OJ;2rBl?K$aYokxE>$3H0;Ac{$% ztXrcpRcuI~H9eD6B0dAZ6sVW_G6Pt+ijB?U-;N>-kpLi;S4`^U?cdL0x&;XdWg0=u z0^tw}`k05_)9!Q5B2E@8xSxHoeV1LqG8eg(_y4$nc{wae(+a^ckxz>m^Bb~b8ievg zC!XNOC!qcZo#WFn0#VlEUzeAcH>Na|%kyk0uqw9pi*%ria%qPq)%(vhTO7%n=jg9* zWgXY`J%wr6e%D_6XJZ|NDRp>_K>yg7F}2inUku1e*f8iYFhpWS@}<9rxSZUTQd|OV zp|PoxuH~U48lFS_8O_>MuoVR%k!Yydh;C89_U2GsM=`b=M!|a8p6j!qWq~7-eP{P) zyHm4%^KI0ZlE^+B{RVpx+q^%Y@dO7b55a5(Y9Y|r8ijy>Q(wV``#g9hD%%*mUS+S|eyTbQ zB*kAp@nwJNCC4&YIB_D00RHk9s%7+#USS@8o;>ph+qepHXe~7(ZSB+LG$VFInMSnU zthg}Fh)FTRKl8C?$(e(j|)DxwPAkmCemeXvz2&sUeK(st0 zw}+RL3Gu{CLFX8qTva+KvEcY&?`~qcG=OZ;FWx{|F)E6?Q7o)%Gzk{zcBpw!3tsmq zXDf%eGU#A2iWe7nj>}} zQ{dWaM_!Md>ezL3*DF24xs*UWg9k9hU)D;t5kWxoVQ!AR%#Z%X1xTkkmF_F#(0D8P z-Md*VIQan#RhBi7VRyUw?5UXYahbPms-rVa9{pqkwBjPw9L;JllgsF5fk~%AS_fYRo#_i! zzX5yx;SsyxBQWld*mz=S+N_F!j#bOLwNe|UJ< zHbvA<(m1!mx$yGMn70`>-^6OGOZHzis%VRMYURL#i@12`8uEaVW5ID>!zjr2)x;m^ zBCG$MFb|WkKOD+>XJs}l%bC4iV)2F8dBi5`-SqS{X8`*{aA|S~!u0x4Z=M8`vLM4{ z#fbWE0b`Do7tmCOl*Qkd@EoJq-NL#ZLr^dMBG;JGSl|09Qh#USH(o^FKHtypt3cu% zaKm*@7cMVdIqB=AYlBdhvR7B_0aUj1$!C3k%nT0a796B(7yU&EQIn)kFY|hhFPYv4 z2{Pczbc~%G?}~t1fa2{xf2MawiK~9Oi2FHs+{gaUDiK)?oN@Gk=R8pqt2!XUg+9J& zR$m+Yq8xP4Z6#Zv`|6w7N|LP-^D<=op1E}cga*Uw+LkR#jjrhe=4W-4u6i?PRSbU$L-=WvG)5$Q;qeE%%S zASfIpUYwr!!&I1dVe2Bf;h9wOaH#+2z4wW^5b+JVGQ9brS+daZ1l+UXmLL3xQoil; zbmZ%w4h1_SbhcOmc&4!swsrY39lSe=OB|6ZT~xCwwh0=Eqw($&9{7kb@4KK^!bJnW zRQfeb>>HRl(ovT4Z=>STzgN*P{!BlO$&e(K`DoK?Vl&a!D*q-=RXQppYJ zzy?s!ANKSrP$`MgU~PO#_-x# ze|_qsEhB1lJ!V)s+jR_R`iIe=o>M>2M%m>Vwy?0Uh>uJ&hE5H~?qa|3>p zjd2Ir?P8J#g=dvdKx{KWVV_spvtNJ&)hGcU@anpp-w5tCTa=N220zepu6Wzea^hwS z>hw}uOl}`j*Z!{{7J{<=EXT^c^U=wir3>&q(rLL(LvUumqnep@oE{!<=QS4=zTp{A z5azp}|7w?I$`s&46Hx-E{{A}EX~e*qqk&8OU$Ln%0v6KFDB~0hdLyz*Sj<0AF*x#A z9XoAlYnwaL9!vnI1s!fPehyBcQR35jfePrZZp&e|}!Zm5rE^fv{5vCet}0%>5N99(vp4UM5JB?^$jVRe9%vtyq&K_2lQ z@$ztK^L!8i+zj#ZPqf|Q0h=3t30f178>2FhGYNWoA>Q`x-SpfqJsCIKTcf$c29-~w zBqY3LN#F3AIRFmos}qp|da;L7xNe<#R;5=u>T;Fz7XTtR&!EFX58S4(>J0_RE0iH! zN0^*apb?ef9oe%qtIjUX{UNWCISh$1wf? z(nL5RiWy!)Th*7Vo0r>7O8BZ`z;TOnn%{eV1Q~8>gd1d?`bR%Rfnwc4qLhA#s0idx1!eRw~2{Nk3V{doXIqw?Gn$H;{gjc zHjMOy)QlX3mijFsyV#pYL)jB0xy{0S2L3sSb51@Pg5>-hYDu8%!aNZ+{mJo7@sem+ zrX0(q%1`bmATb`W%vNs&V?`N-y@w)X1ZuC|<7Im6$B+~by;#$XBptV+#WOc*GVB3* zlT<0}O9oHpqvn=4s8JXJ+p7EVHa1f_{eq;8BXV5O&DJHq_b((bjCNwu*t{hmCMGuY zoWifNliR(f8w8%UYgE!)K!$qlH|{|rHy{GSj(w;l1p7JX#`9wrmnR!0mxoHy7QQFc zzJmP-hIC0|wN@DHc?m4UDkzTq;{k5}?Q2Dqn1(@tpRsA_q=t+tM@? z2QG~UtDAH{#Og~t*gfkB+(^HQorH>+as69^`M^13IR5C3{zbGF$QVJN7zz+)&sx8# z$1gfx<+%pv@K>%}K`{w2DyWMMR@B%tzQmPX?gVJtZQyK+Ac>-{-#B%#oI?V1S`QG1 zFqwkwscc$n=PkJmR$N~tc@Y*91v0@X!9gh1q#(z5_lZPh8it02(eD2I<@s*4$l7ZQ zWJ-?T0U`M}NFINGaMA->iMas|;79vc#O85zdE$PGm0y|1HAGLQ)S%*&JV@pxPk*6P zG6lx_>KM7;xGiAYj#w9UMoS|Lh$s4of#9<9^m5aY0Sn$8G$UqQ}`)Oe2_1{>$3cao;yG`Bswi| z&tBYF0UiWL^s@l<7*tV@pUjp)j}wEVdTn$gjVabaNLV;VmQvB=tAV0)z-N?lpGja7 zHBdp0{Ey(?BMM^B8OMvAy|F??LfRIpr85C3HpqMIWpMyd?1sdZWKcsw> z-+j^s+#j$9VB%QPhfV=-o1TrqQLgbRc$9!M3(9IOXn_L|2G5M%*W+r zB`$Ruoco)xdhHoZ7IJJ=GWh_w&{JnwR%dmMk*ClG9{5e1u` zIn!R}xos=J$M^Ado8-1{sIN7t@-1K_nFGmJGk{}OS%dd6LtS)?_gl_~#h;=ld5Us; zVt>$%%D|o%D6p%41AZgYD5)B-Y55R*YR}Jx7R`A+7%KMHBRXAz_V3MfDu8?@d^`zhC3P&O3*sR;YPXw7XP2KJ+JMN}M72eji9Gyo#EKx71k zfwXzi27mDSQvp(WkIsuhNSB|V{VYZ+O~p#mE_dT(N{r8;?mJ3( zNUE_PZ4D?CHk@CUpy(NZgmP-Y8%*^)C&w-YGUF&NLiDSLh?1}hJ_JP}@59T;MG*I0 zWq&gPjaR;v{B!+dRNju*($30g=z(cI*a`$DUj{iIM6dzP&|%^z2C27B{ic{(uZe0`K6_XNjV%f0pQ3SdeHb1x}^%+&wP9 zaMUsk(KZ7Wz7}>`&Q8iA5ivtxx+Dqc=8oWEf=_&+voO_AwX5CMQYZ>TO8@!|3mW@( z=?6A1MW{l`41Raa84|#n-Tyf|CWI*Am8$&0hUd>xXF7L- zOAj4JE4bg841!+$4vUIzCh*5?Zdn5;On7l)qNLYLUlmaZhWmmC*mAHECeX^y#g-iV zV?jI$2|U8-?_pC`#zZp)8ArSy8jDUp+)NiHR)Rg6Jae5DaRioi5Iy4q8LT-pBN^>q zV;?rc1Bf^IKb9&VQGfxZ}qXqz|Zx zELXB*0aAfz!QpBdq57FSxoXF=wJLEx;xNUP3E<9(AswPWsFm#xqA#7rZ0xY9=s}J0 zTH&m(U^C&^8`Dn{3VY_?5;w*AMvutAZWMfBckBRG6L0jUngxgLf#wECXs_;X6XE^^ z95E&=$#wVhPD9P;cpIoJt{)Sn1)$_1R!^}eI|Y=fYVDcFjh7u9b|i?mkHy!}doY*l zCmB{b-J`bNPU|jL_n9K-AXv3bJTzwhG8g}kOS`D_E6E%@qm_>8c(~)!z4z^_7|-cR z`lYSBloS>a_-p}bSoi3gvf*C+M{4qU00j1g&=f()lLW~;kvd6-5F+wWW>2{&(CYsE zHW07VI6gdS2A7v!9#DYnCjH-C{kuGWRw*c6s))BhH6z0W56LnV8^OWMlR(FU1b!4n z=YR1k=R=SrKrO0)Xu%&T;35`USszrv5D5ragFJ3M;)dLP)6j}6Gn+okPtQmtSg&mQ zDsHy|7arB!(MJJCUZhdpNO7ohvehikGzkq2-9FGWWqyv0>n$q#YH4Bd`Dym?!73jLB}GQkovwQ39P z{VOt|$-TX6R~DT?tLmQ*T>%eWQK`K`<1d_PR-$cqwLo1I{Xd8o7)1d@0hjP7hEZ)L~*^hQhlvNMs~ zHeQmJW!OqWw;B`pUkA?ZQ5iu7nEEAH*#!{?ye^5*kqYa5aOa3Ww!RsR1yUC5FGow; zV9-efd79zpxgG+NfLPyh-y=^ZEzyT|08Z%vD0X_UUKAAwVU!r}O9SRqyGVbH-Xeot znP$0U31IRb_G+QP0pdw)ceiFEU`t&C^??fw|tTs1<;@Qm|*IpMGgNCw}W^)-TB;P`Q1+rKYA>h}{5=4v^A6|DX20 zG@h#V`}>^Z$U(*o-4v&!NHiT&!pT^|jSOWTDpMkJ=93{MLnX>Q%S`4uL$@Lva|j_b zQN{?*+V1K1{Qs|?H_zwStFZUJ_H|urt!oY6_1)=0Qwk{3Jmrd`17%nYgGwYIxZ^Zi zpVs;;&gcxRKCTJ(mQD)5odS0t(HoWk;|YUq`i zK#JfqGnTfuy6YWOrp95X-at;^R;hF`^!Q6*aonB>-y@D;J=PZfDrUgrA-q7c4P;-R zcFrQM$D^yM**}+>IafcF-W{^KTe%=Ib?fG!!Bk2NBPj(Qc14uiA0DQLso?J#(+BT| zdFnzUrH5**=?agdwY?;Ej17*lA*pbBP%lQcjh9hT=LX^k>t^GB1mAXaB&=I$)Cys0 z6xI)JCsZEsdFz4LWW-CmE|-w9RM#-tyu-HvQ=C@p-+ny6Lx%HBrWSEb*udQ(3o9`# zt)5X)kXD+x)O~<7jKtaZzgs~QmmIMxhp^Dx^x*R++;2YvbL8tI&HrrcYWwN`+qRu* zGd0tDBuOTJUV|DGk)OC>m+_(8lwIw8?}`97Rr~f*VzW5Yzbpfcc63;X|+u47lvb-%oo^VW?}K(Vv`jKDf7c1M5qB_lBbzY zyQ&u&vE=9=oL}FBWb+(;6JreX+1vr=CDtrIr_ez|HIV2G?=&A3P6KKeOT02T&nz|T zCR<*+es(pCiXmb!{MhvR8iS0Xf+u{(li9!#lVM{BN zi}6VHXVc~~9cWP^@rs1OBe@X;>wy<(M)&Y!{)DL^i|yxgzBw)iLH1JEACM~l+MnAE z#m5WS6~R0ONy4Uj^Q8^G)YPr4^j_)K*4EYaNy=g2bLTWD4dFZ>Ca8jYbfR$)2C_u^ zBY?m_Bgiyqv}GpZd%N@+(J zr%!${JDDW22yA|gNVW3cqT^*15WAw5-z}*|WFmXVs{8ty3Z<`m47*D#L0>RvLip#+ z?!zQyN{>6IqcE(aJpS5NrB6;t+4jX}@%X&m!}Vg}uu@29T;(eHUbLD~<3CA+It2Hq zer2}JKoPYoGK&Im3y`oGMS4jI=@}y%f!WRSeP{dnBWzY@dlZtn4}rZ%Xb*_n9^Ewl z+KL1ScyCzP9UEmjruT+#o00}J$vEzeBF(Ldm{+edN|K#DExwntB|0DmMg`PbCDj$u zoN^};Y#oJX6FWkg8R``uXILBIsyaY=2G{YHN;xUwO-Rqu^)vcir}gS% zOZ}6!bUoT4+64*6BjxabLAbmx_%bUapGv}4ejO`o!Ofln>95u_blVqeYrw6ylhP0) z5N?Xt4uAGebUgtuKFH_sm&{~{VP>eOsoMtLrFZk|_=T=)-r3K)w))i}ZktJVPGcXo z6LvqP|86)3X$(v7mm5uPG|0VUcK(ODeyNLw3Af&2rtyHAj^{I^elrb4_Rg$>d0?p$ zFf@k-$`v6)_mk`>pi(&&koVP)>^`*mvt!JmuP)Zydu5od3U2xaegT;iLO@-#N^xgt zX_Sn{50I_$RjRDYyj6{h8}|9mqeHlG$k6duQ>l4lkFt-SGsA{%l5XU7)&wR=vYxTB=#QPw7 zdwqqBsc_W^(H6km1#_+ZK_W_Hq_{4=wkMz7vZNb^Ln;j>-}FNQ07mN1TXfE|!AeRc-T#+V{B!D;&ip5U)dduQxpHgc{u4cEZL8s*qh7=rn01;K93apeVUCoaD& z`9Aw-KyEo}UN$@VKzs2{LtovBfYF;6cG5Yx?pBFJvBKMOpSxZJNr3K5eq8^=I;o4eS6JTsC7on(gKKAL&a#W%=QGio4l(59GZ^ z%X{^HUO1Su%-eFGi4PoDjk|-_9I*SC$)&k{M&6RNAKlfCd93)nyUIyzp=WKg@@3h0 z^97QwA-)2+IX4)q%R??j)g?<`s8@smtxeD)wCzr}(u_JNz#oAE+g>scFN^MVT^V@| z8K?!>97ok%Llv4oRR*1hAPt}#T7uPi-)^BL1n+pMEi?Cc^k@6V% z{8jb$jrZ05gIV#526=Z{p&7fiu~E*Qb)p#}o*X#G2GdeaaRheq#UD__x$hZvW4SYB zSE*ic)8?Qxorknte&Mj2cJSsoh!_`b)%3g9&ImKIbX^o_0mhrEiZWow*j8}hUg>>4$GQP^5xey`9#fa5_7dm ziE~u8O}X}DL8-bq_8*jZ6^`*jReo<=_>%h7M%qml3jvSUWy-a)g&nS(7#snXmXJ2) zk89*5PFs{p;)enbxt!;JwyX4aF&6ioGmb=wWUEhEBVG%ZwztS}aPC9YO_XPUTZ{2v4TC!`f%k|^@8p*}zZkwcM`T43W zbB0&xl2ab$Q?yP=f_fzfSKX22#QTBjKz7=~z<8%LDOOb4xjCGsoLrijqvM0Y9w4iH z+AGkqgiK;$d03sPM7R1cxUEp< z^JP-|ub^v5<~u*Ctz9gS$cgcup1nzP80wKc9;Z5IIWqF&&vMRR8$K6>GR=Hw^`q?T@!Ca#G8w=o!!33UFuBGW8hiVV*GMp^ZOAr@*S7>>3nAU>kv4WJ+ zer}F;`GYdnjy!z{1!|G}l0HiwoK4=Z>@Hv#Eud~-+1MEL_c1I(8}3Mv*72lmsw3s1 zQ?r&dTy0V44~#21F-XVH|Gt6dYt8Cw1;YHY#ftgq!LEQi4@L5{>`@)ERI5vu_Ex?K zR=&vfQXh{GOZTToT3Xj~SfuRrT&*b{a=E2Hr4Mls}kmzh%{F7a+#U2xhqw^ znzj{10kX-QfVz5aW=GPX2$Z6Q=$qqE*!jIs5sdUEaa{0k!1FelZM<0T(VZ1 zmJ%JlJkR@NIu&3_{_>`-ghwH`0ao?*dl6xZoke1qg6QPrWG3a;Q-{_Ety1?ff>&8; zbk7+=H0b=6Vfg;n4sg<>y!ljh-tkOvrpfpPw;PYpDJePWhn)_nf};`wRx#v~_|Rc+ zSlnczF#!8m<%*i)8ox9hn5+ejah#%e2|%ZFkAOlF3P<5`c@g)QN(YGbVh%5!Qz99> zC8qJ`t$;t&Q^_+;;R%V;(?yN;bKhH$P$Cg$z~*$i`!qqz#KkfDSxQP%j}lU^J#+}= z(Hzd63CvoKU;}(8m(Ff70k|c=o)=G0bVu(YUJkL7Et(MOYj7!8P7a)NxqUkgm>SJP z#3+?$@u#Fh3%NB;VwDbn8SEWH&s{}P2O4ao7PTSJMfL1;GK)a4vqiTrj~D3~be<&j zcFXOB8VKlNP%!NKDm5N*BXU|@AyQG=os({%vVgeZUNC;FB^R4~NgX-~!T%>8afN;z zEI3tI6QSj!$T=>lnKL+S+V{;g)l!=Vf5?(1oEbs5-rh~=#(jkud?>!f^O_xf?194j zBAktC`5S8qLeJxlkQIsR{AjtkWPMeJKkzvT+s4DL5%QD2{Mq` zo%5njsX|A58q0*83@Vr0AzT52{LE#mL{|cHIqTOA1O3W%i^wdsWG)&`_qF+q2ULo~ z7+x$RQWE%6mj7gMc%Od=8@8WQ*zN_37-2|hB(Qb2CrWDqn_RH-IuHS+JDknR3O;(OzTNI9!0xyfVS!_Dv&?$VKBHD(DTG=uCQUQ z$*nt$9INEe_J-U=av)XZji2EmTc5?L!hl-kytw#pE);5D82F%)rE#FZ;p4_^aoL!wg_fzJ^$jP&W&Mrqw>9_RSDOj#Qt% zNe<${ZN7wM4S6gxoXN*NyB4s=BIG^q)TM^_EFTIORjyc^5Qx*}Qq-&D^A|ZzzTE%I zY@+#v=fbnKv>-$Cl9-Zi`HoBneM$HC7Z=2*BqWS#PmI<1{z<%pI|BXwiTcyRIK01z zM}yUzg1xC}eM{G?;xq)=79n~k%;$78_fbgmhZ-4-*556yg5E#dx^BZI*3({3e-@1L zwpnq@qSRmZ^;RB2a;Mp#Hn79K(Byj#XsETwy$Lkjx$$#}5C~Awwnz#awpuzZXK@$j zDDXO3gkM#xO=gj@erAR;S$Npa)ld!&r9Yh9{zs>#K_U~)KC+X8GtvSay~Y=09{EoQ zu|&tqlIr*E=M*#D&#qz>t_L?NKG&Xt8T{Wpwxvy(Kiv`%*dj*k8t862JC`&ufB$g- zEuo@h(y`-^dBm)sVv-FO_w`&qQcf|2*sPIE^D2UbdS=r{@dXzg3v2 zOP@P+YEg0F>-r~{zC!8Y)X)0-!G&L0h$1!vU+0%26z0^GAgK(oB(#R|s@=t0#sq&& z4BTWhp#9kN#~(2<26GZc!MO^O*RKzAW~8b1C#Vm+SGbm;554Lf#WjzSNc8e0xLZbm zS62aZIf<^%ynyxM%9%?;xN@3_5e9t@tNh?Cdl&NOuL;AI@A60PX4M0>~lo9{4 zm>PdyG{5CbV)Pk_*2h#-Lc_L%io_WkEWK#_>G(ayin9oVgZTDJS1?!RYexI6h0?um zj{pV7>jAmZrO^foEkFM$itpcV!%2Dt0&r#z>PTM5*fb0IQgvm|ZUt7c?&Y2R>DiN& zS9JuZSb?+v^RT|Dv}RRs`Msjl4?q5U$OkC0N)8e13YV&4YLrx%-ovAwU9(ouW`_>Z zE>1#fqJx!h&6UzF&J`7&hZ1O=I$4{PDQ5r>jbI#r$}B?@jJb*KixIA|^F9{cverpR z5By7neqqwa)?H6{LTP*FOPw#m_{2dsvrdi@K$y!=m<&h;YgiZfHP1=-@VS19^<0s5 zqh$7se!ZJGm_Y|k?pH6LYD=o1;i{NHz^8U+7bR75cCMx-Bt~ob)3b%HFMgVn@T3Zp z7@AR?l;z%3D}GfkVDz)uYT;q*1E=LyQ5}<%_}cVtBf@4KVyRQ0GIOP%hnc^y9hFqx z8_r$*tbkAb0aU?EQ{>o92Yn_eYbg zd9MEVmwdVJ+^v{Ni(^lk6PMVu_t{g9e^e_T7(5(25F!bl!+Bn-SprX?uJejksO;@t zpYb1@w%L={9S8@=txxY-6_Ly1Ws_B%ZJH8jt+&d(J}#SnTn#m-~?=XN{Iul zVX)Vy>t6zY=t%SxoH;yJW z&mE1-K6R!1P4q23k6iuoPUPbww?S$q;cG26tLvIwtp`_jK79C4lP*`$H~s`M$KJ37 zJD**3D3F^+I_D<8{aS7pW$@l;Xa)HpTX}m{3dtoO)m`~;t*BBoJccmPu^3#S5J4Cq zEqdk>ersy!bys!G(E#$K?nmqVT;IMGWw4{3iy@h%61pLsU z_45NJvQ%>yRClTd%$?px@M~Lcdrrbqf0f-Hg%90?-XGZ-)~Vz3s{&mkAm8F?g(mXM z_zHv3?pnf+OCw5Iwn)?Bb?8=+SzJ2)1#T7njWP`3SMQNofw7%>M5;E^z9UR2Mms+u zZ%~*j3u`q)=;G`38y zsjzYF%QG5Tx46ZFHS_}&!@{YI-FJG%9=3RBSi_ouKso0SzolPH+Y6tCQFeg-@`kF7 zcc*jHs9sdiMi{sxkULrk_{ucdk>+!q9ot>a7K(=6LD2cW%pH*_Y(;}#Gszh6aw%9! ztSNC`(5+WAGzbB7v(H(;Pl)bKYufwkruKJ)@CIqx{)l{9<>Va;y_P*nmff$b3!Ycn z(KDis)6v)c>9z?5`{#1H;SKI*Yn$CDG)>B08LW_>#6^4;*v^jc%adco%wZZp&okFUdXuemW0 z5=>JJV?C(Ces*zcG7aeu&d_mCepn!vE)AfStr?f3{4CVGFmEptwq4|u%yN=P9^Athp+hAqaC|;<?|fxz;Yw znK<*}!i8GDd~cnUSp{&xP^3#>x2Xpu*!QT8Sj^`mGiW~6M^Ihne0Up6q!2cLe6pyNn52)&p@l!W9Tdt&91 z+19+*HqAXHGGyDC3a%Ynp-VIPn$tleVR~<6T0JhE$x^YnE4%DzP!Z{RR+h9O|nM{G@mljv)w-hcV!~Ce)QFt2yJh+-u@66eb`X@Gr=a#y;gWq$ff*h za%}vCbRwT7-^J|f1!I0r2X$TQp4OmfTe@r;N~}QeW*p&57>uK&mo}^PxsX?`fjQUQ zJb%GP#YJ9NVekrJ+yx?WG1ynlSjxMAFiMh9m?5^P0AsSAE}E{53+1dOuJ<%=eKvb zse|8Qnk6o@JY4PG3docgTk5N*d!N-P<3nanP>R+}Wu_k_o_eH!5xw)t&f`2?yJif6 z9I}J>`kun8;YWnd0zm6d)io%=+sz#~`{I*VOO~zMeLG`i!RI@dx%2 z5v<@Lo)nzOj~Sq|>dQ6g*`$vL9B2C7nyKDzw<=zBzNVtW)KJ>v@aH-Q!VtRDUQd^U zZBV~dg@si;*d_ibO?T$R9w0KNU3ldq1tkO5p_ji$r>Q*$e>R(S_{515)JOOoFE$4f zM?sfmF8C-&BE6I=qxX@Y`cOSZUD?cgkvkYee)G42p-Df2#;7<>IMLpln_KK9Ej+#-r2m4w4xrTp{eTtoW?p&s^ z@n>%_*P^h~wVUy?AA{Aa7BqD~*zd=S_yM!stC{PmP8PF4#G~+bavYiQrfnZTYF6qO zJ0Wa4p>q|^>gDwi5x(mY3IF4zK1JX_gz%9SJ20}~g+NE<{_8@)Zh$Lm1)O1;S7o0z znY?mR@T9qoVx>)84?FvN;MkcQ=r(~uwj%|Sbc63hUe;_KO*>-~>hVbz$bX8=mCQR- zwK-;tZ4{Mqw4HA_FyO5WB+tgCr$r96XGmfSz{51Peu{4Cnv4qlDSyIe|EHIastmd= z&OfkEyUyEb!LnRNq(;lV=;6cZ>34kx$Nx`G$iU$%5i4T;zA!&8>gyx z@*p7A@STu0iCq4~g#-36x?soYhfhOp->y1oK!P^f zO4HOq9e#0)IIIA(Z7aakUoH%mDhjVQKVv3#PU}Z;=AL*iU^hMKCw$nlh%?oZM@<*? zl!LZ&XytGl05!H@KAWXMW3gRbT@WscJbv@>G!}iV`E~})t#s}FFHE*q_rAW<) zkZV1&V*ol`NY}{gQD82+tH^$_Xpa*}=T)5g1D*6`J{#g4MdiM!7 z%r5q7&^zHbCc8HeD~h@2hL;dC&~iB?w_4a58f3e^)rNDCG~rbqmFPbi{MMF!%x7ap zpN4&z;$ghNJ#Fj{c0O?*1XSmi@F!vIUgGa_-6}XWU3sPm^0PGT!5;wp&ypY4txfcv zD-~Pt7+-)u`N^J~Z&zS|URhL>flcGX@m17Dv zoo)&-wU9IGe{%~P93iEs&xWSv^NI1A|GWh;jO+CN7^p`a;hR2nV&Swnw`QiPG2>~ z8J#@L#kJ-nwkGR?BI!`$@Oh>je=x`|1)|RRF^xpYGX`2e++AsVRJwV4u`g9s*6s`y z28#n|o3pd_U0YlBA(g3x@hjK4+&W&h2VilIjEuw!2|WYHaydh6TdYFM3|-?28H=Z~ zfM*CBaB68~W#!j|#xFgy`Go~ZX;M3$=tGPEVdWi?jcsXZVUE}35i%bb8Ch4kkwd8# zi6V8-bpP6}sjfB{Gh@UP@mC?u`0QI^+lLQVdN|o)(-uvb79OC`nA5n%{!rIOx8Nf_ z-D*0|-xg6~!vcD1C+Vhlh{D}`X-YoC=a}M}n?JUyKRBPY#HavLk4|s|pja>Z|4^B{ zx5gt>cMb<5e6hg_2?=FZao==sD4hTHt)}%-KS1QMhA=S>s~}x;V^AiC74tP-V!{oQ z?mDUQIpz3{6hN6VH!zsX5i1Xz#umy8`|s3MI}U!X<{tWd2cU+e`&ew){i0u|&=^4& zI5>NFXvjcZQZnb9-sz0Y`dmIV=D-0);_;F%^pS7<;MEn_j8K+06P2Nr8*>3S_tpax5*bU@E*4l86DY*7PxprA%z*#K}cAp@ldC~8xs+j?ys0$Y_ z9x8hNfeC*`3N|)TxVpTY9}T? zEpP8yc?E~#!~_%M&UZo5nntx=hd(~i(biVFcw}y<(xu(uvGmP-Ajr61wM6~ftnnL% zxr~mzOLtz{5SBGBb^;j}28rO9rR~nX&$Ef?D&a>i-mmtSZkj@$<=W9RXn1sid`(%I ztCWo`XbOn6Ja+7lW}WF)WD-WoG*NRY(l zzW-dFVm^&0z;xBF0jpQWi0ndodiuHO)++WCs;iYSgSRg!tJcjvX3c%V6~P3GiVaV< zt$q8J8xt27^5ya&46+AR{%gYV-E?c3{Z+L}aPnCSmT4G&MIjCsh_m!c*PAFt{aW*Cv;7 z9~%zi+U0jwPGqrTIuA{~cLFThq z1tA#8j^rQ4B$O$86vjA&sc$-T-76eLazq1V)W!5Kt*t7LL{lXWJR8rUfl&jF;yw1? z;{E80B1wa~@qD8*mr+1Xhsg7cXWlJ*Vh82_n1`+XG)bA6dHZA zUXpQ?=fPzU>{Dvd2eKy+lhD)CGj*QJ+S*#>W8X2@z@-ytw4Ck5hYXkGc#u_piFwfU z=SEJ$k~)@hPDFrl;x#mNb$6F&9(PcK&9S(Izk4#&#jUZ4Zc;G&r8qPGOenllXs@4_ zNZVsM#`4W8BPR4i#NizIW|sceHR;Yt$2ZmasdqvZ)VUR7b<{xAIdEq z7azyEyEWAw-@Z$8TvB!rNCcAE0~45d>J=`wo~#epx}Z=yPalRNjQ|300kjPfMnsl~ zQK0JzIgoew^Jn-2CXfr!$G?7qSAoQFvUcF#Kf@oOF&DY@uiu0n2dJF70Q8dm{R|&e z64lUqM85Q2a9-7MB9zW7$o^^zSqAKGT0c47sNh#E4+!{~ynU zvf|$lL!N30ip^0h^B>Q|;obfj4wj1mG90XX Date: Tue, 26 Feb 2019 18:23:56 -0500 Subject: [PATCH 2/7] sankey: fix syntax --- src/traces/sankey/render.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/traces/sankey/render.js b/src/traces/sankey/render.js index 09cc72b0965..3ae40953d2c 100644 --- a/src/traces/sankey/render.js +++ b/src/traces/sankey/render.js @@ -69,13 +69,17 @@ function sankeyModel(layout, d, traceIndex) { Lib.warn('node.pad was reduced to ', sankey.nodePadding(), ' to fit within the figure.'); } + // Counters for nested loops + var i, j, k; + // Create transient nodes for animations for(var nodePointNumber in calcData._groupLookup) { var groupIndex = parseInt(calcData._groupLookup[nodePointNumber]); // Find node representing groupIndex var groupingNode; - for(var i = 0; i < graph.nodes.length; i++) { + + for(i = 0; i < graph.nodes.length; i++) { if(graph.nodes[i].pointNumber === groupIndex) { groupingNode = graph.nodes[i]; break; @@ -100,7 +104,6 @@ function sankeyModel(layout, d, traceIndex) { } function computeLinkConcentrations() { - var i, j, k; for(i = 0; i < graph.nodes.length; i++) { var node = graph.nodes[i]; // Links connecting the same two nodes are part of a flow @@ -167,7 +170,6 @@ function sankeyModel(layout, d, traceIndex) { // Force node position if(trace.node.x.length !== 0 && trace.node.y.length !== 0) { - var i; for(i = 0; i < Math.min(trace.node.x.length, trace.node.y.length, graph.nodes.length); i++) { if(trace.node.x[i] && trace.node.y[i]) { var pos = [trace.node.x[i] * width, trace.node.y[i] * height]; From 1a89a1edcf894d052e292e83ea5fe50de04b98d9 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Thu, 28 Feb 2019 17:03:18 -0500 Subject: [PATCH 3/7] sankey: reset node.(x|y) when grouping, test drag for every arrangements --- src/traces/sankey/attributes.js | 1 + src/traces/sankey/render.js | 7 +++++-- test/jasmine/assets/drag.js | 6 ++++-- test/jasmine/tests/sankey_test.js | 9 +++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/traces/sankey/attributes.js b/src/traces/sankey/attributes.js index 9c78e859c63..57cd56072da 100644 --- a/src/traces/sankey/attributes.js +++ b/src/traces/sankey/attributes.js @@ -91,6 +91,7 @@ var attrs = module.exports = overrideAll({ }, groups: { valType: 'info_array', + impliedEdits: {'x': [], 'y': []}, dimensions: 2, freeLength: true, dflt: [], diff --git a/src/traces/sankey/render.js b/src/traces/sankey/render.js index 3ae40953d2c..d7fe72f2adc 100644 --- a/src/traces/sankey/render.js +++ b/src/traces/sankey/render.js @@ -641,7 +641,7 @@ function startForce(sankeyNode, sankeyLink, d, forceKey, gd) { window.requestAnimationFrame(faster); } else { // Make sure the final x position is equal to its original value - // necessary because the force simulation will have numerical error + // because the force simulation will have numerical error var x = d.node.originalX; d.node.x0 = x - d.visibleWidth / 2; d.node.x1 = x + d.visibleWidth / 2; @@ -744,6 +744,9 @@ module.exports = function(gd, svg, calcData, layout, callbacks) { firstRender = true; }); + // To prevent animation on dragging + var dragcover = gd.querySelector('.dragcover'); + var styledData = calcData .filter(function(d) {return unwrap(d).trace.visible;}) .map(sankeyModel.bind(null, layout)); @@ -808,7 +811,7 @@ module.exports = function(gd, svg, calcData, layout, callbacks) { .attr('d', linkPath()); sankeyLink - .style('opacity', function() { return (gd._context.staticPlot || firstRender) ? 1 : 0;}) + .style('opacity', function() { return (gd._context.staticPlot || firstRender || dragcover) ? 1 : 0;}) .transition() .ease(c.ease).duration(c.duration) .style('opacity', 1); diff --git a/test/jasmine/assets/drag.js b/test/jasmine/assets/drag.js index 6777ceb2204..23643c79b8e 100644 --- a/test/jasmine/assets/drag.js +++ b/test/jasmine/assets/drag.js @@ -1,6 +1,7 @@ var isNumeric = require('fast-isnumeric'); var mouseEvent = require('./mouse_event'); var getNodeCoords = require('./get_node_coords'); +var delay = require('./delay'); function makeFns(node, dx, dy, opts) { opts = opts || {}; @@ -49,7 +50,8 @@ function makeFns(node, dx, dy, opts) { * optionally specify an edge ('n', 'se', 'w' etc) * to grab it by an edge or corner (otherwise the middle is used) */ -function drag(node, dx, dy, edge, x0, y0, nsteps, noCover) { +function drag(node, dx, dy, edge, x0, y0, nsteps, noCover, timeDelay) { + if(!timeDelay) timeDelay = 0; var fns = makeFns(node, dx, dy, { edge: edge, x0: x0, @@ -58,7 +60,7 @@ function drag(node, dx, dy, edge, x0, y0, nsteps, noCover) { noCover: noCover }); - return fns.start().then(fns.end); + return fns.start().then(delay(timeDelay)).then(fns.end); } function waitForDragCover() { diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index 489d40a4f3b..65ff16abdb9 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -1012,7 +1012,7 @@ describe('sankey tests', function() { }); describe('Test drag interactions', function() { - ['freeform', 'perpendicular'].forEach(function(arrangement) { + ['freeform', 'perpendicular', 'snap'].forEach(function(arrangement) { describe('for arrangement ' + arrangement + ':', function() { var gd; var mockCopy; @@ -1037,9 +1037,10 @@ describe('sankey tests', function() { return Promise.resolve() .then(function() { nodes = document.getElementsByClassName('sankey-node'); - node = nodes.item(nodeId); // Selecting node with label 'Solid' + node = nodes.item(nodeId); position = getNodeCoords(node); - return drag(node, move[0], move[1]); + var timeDelay = (arrangement === 'snap') ? 1000 : 0; // Wait for force simulation to finish + return drag(node, move[0], move[1], false, false, false, 10, false, timeDelay); }) .then(function() { nodes = document.getElementsByClassName('sankey-node'); @@ -1072,7 +1073,7 @@ describe('sankey tests', function() { .then(done); }); - it('should persist the position of evry nodes after drag in attributes nodes.(x|y)', function(done) { + it('should persist the position of every nodes after drag in attributes nodes.(x|y)', function(done) { mockCopy.data[0].arrangement = arrangement; var move = [50, 50]; var nodes; From 370250e76523e07d05d600592a56366c8c4ef0b0 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Tue, 5 Mar 2019 12:01:37 -0500 Subject: [PATCH 4/7] sankey: in `snap`, separate overlapping nodes even if node.(x|y) is set --- src/traces/sankey/render.js | 73 ++++++++++++++++++++++++++- test/image/baselines/sankey_x_y.png | Bin 34933 -> 37713 bytes test/image/mocks/sankey_x_y.json | 5 +- test/jasmine/assets/check_overlap.js | 38 ++++++++++++++ test/jasmine/tests/sankey_test.js | 41 ++++++++++++--- 5 files changed, 146 insertions(+), 11 deletions(-) create mode 100644 test/jasmine/assets/check_overlap.js diff --git a/src/traces/sankey/render.js b/src/traces/sankey/render.js index d7fe72f2adc..cbb37af9b16 100644 --- a/src/traces/sankey/render.js +++ b/src/traces/sankey/render.js @@ -168,6 +168,70 @@ function sankeyModel(layout, d, traceIndex) { } computeLinkConcentrations(); + // Push any overlapping nodes down. + function resolveCollisionsTopToBottom(columns) { + columns.forEach(function(nodes) { + var node; + var dy; + var y = 0; + var n = nodes.length; + var i; + nodes.sort(function(a, b) { + return a.y0 - b.y0; + }); + for(i = 0; i < n; ++i) { + node = nodes[i]; + if(node.y0 >= y) { + // No overlap + } else { + dy = (y - node.y0); + if(dy > 1e-6) node.y0 += dy, node.y1 += dy; + } + y = node.y1 + nodePad; + } + }); + } + + // Group nodes into columns based on their x position + function snapToColumns(nodes) { + // Sort nodes by x position + var orderedNodes = nodes.map(function(n, i) { + return { + x0: n.x0, + index: i + }; + }) + .sort(function(a, b) { + return a.x0 - b.x0; + }); + + var columns = []; + var colNumber = -1; + var colX; // Position of column + var lastX = -nodeThickness; // Position of last node + var dx; + for(i = 0; i < orderedNodes.length; i++) { + var node = nodes[orderedNodes[i].index]; + // If the node does not overlap with the last one + if(node.x0 > lastX + nodeThickness) { + // Start a new column + colNumber += 1; + colX = node.x0; + } + lastX = node.x0; + + // Add node to its associated column + if(!columns[colNumber]) columns[colNumber] = []; + columns[colNumber].push(node); + + // Change node's x position to align it with its column + dx = colX - node.x0; + node.x0 += dx, node.x1 += dx; + + } + return columns; + } + // Force node position if(trace.node.x.length !== 0 && trace.node.y.length !== 0) { for(i = 0; i < Math.min(trace.node.x.length, trace.node.y.length, graph.nodes.length); i++) { @@ -181,6 +245,11 @@ function sankeyModel(layout, d, traceIndex) { graph.nodes[i].y1 = pos[1] + nodeHeight / 2; } } + if(trace.arrangement === 'snap') { + nodes = graph.nodes; + var columns = snapToColumns(nodes); + resolveCollisionsTopToBottom(columns); + } // Update links sankey.update(graph); } @@ -720,8 +789,8 @@ function sameLayer(d) { function switchToForceFormat(nodes) { // force uses x, y as centers for(var i = 0; i < nodes.length; i++) { - nodes[i].y = nodes[i].y0 + nodes[i].dy / 2; - nodes[i].x = nodes[i].x0 + nodes[i].dx / 2; + nodes[i].y = (nodes[i].y0 + nodes[i].y1) / 2; + nodes[i].x = (nodes[i].x0 + nodes[i].x1) / 2; } } diff --git a/test/image/baselines/sankey_x_y.png b/test/image/baselines/sankey_x_y.png index 3676157c6f99c2230cf4c5a99b59f5f36ab06b21..f769d35ab57df3d9672f636a36a3eaa18e145e4f 100644 GIT binary patch literal 37713 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 BrHes&Tp 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 literal 34933 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 compareRects.left && + baseRects.top < compareRects.bottom && + baseRects.bottom > compareRects.top; +} + +module.exports = function checkOverlap(base, elements) { + var baseRects = base.getBoundingClientRect(); + + // handle array as second argument + if(Array.isArray(elements)) { + return elements.map(function(el) { + if(!el) return false; + + var compareRects = el.getBoundingClientRect(); + return compare(baseRects, compareRects); + }); + } + + // handle HTMLCollection or NodeList as second argument + if(elements instanceof NodeList || elements instanceof HTMLCollection) { + var collection = Array.prototype.slice.call(elements); + + return collection.map(function(el) { + // check for holly or null values + if(!el) return false; + + var compareRects = el.getBoundingClientRect(); + return compare(baseRects, compareRects); + }); + } + + // assume element is node + return compare(baseRects, elements.getBoundingClientRect()); +}; diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index 65ff16abdb9..1ddf9a39821 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -8,6 +8,7 @@ var mock = require('@mocks/sankey_energy.json'); var mockDark = require('@mocks/sankey_energy_dark.json'); var mockCircular = require('@mocks/sankey_circular.json'); var mockCircularLarge = require('@mocks/sankey_circular_large.json'); +var mockXY = require('@mocks/sankey_x_y.json'); var Sankey = require('@src/traces/sankey'); var createGraphDiv = require('../assets/create_graph_div'); @@ -20,6 +21,7 @@ var supplyAllDefaults = require('../assets/supply_defaults'); var defaultColors = require('@src/components/color/attributes').defaults; var drag = require('../assets/drag'); +var checkOverlap = require('../assets/check_overlap'); describe('sankey tests', function() { @@ -510,6 +512,32 @@ describe('sankey tests', function() { }); }); + it('prevents nodes from overlapping in snap arrangement', function(done) { + function checkElementOverlap(i, j) { + var base = document.querySelector('.sankey-node:nth-of-type(' + i + ')'); + base = base.querySelector('.node-rect'); + var compare = document.querySelector('.sankey-node:nth-of-type(' + j + ')'); + compare = compare.querySelector('.node-rect'); + return checkOverlap(base, compare); + } + + var mockCopy = Lib.extendDeep({}, mockXY); + + Plotly.plot(gd, mockCopy) + .then(function() { + // Nodes overlap + expect(checkElementOverlap(3, 6)).toBeTruthy('nodes do not overlap'); + + mockCopy.data[0].arrangement = 'snap'; + return Plotly.newPlot(gd, mockCopy); + }) + .then(function() { + // Nodes do not overlap in snap + expect(checkElementOverlap(3, 6)).not.toBeTruthy('nodes overlap'); + }) + .catch(failTest) + .then(done); + }); }); describe('Test hover/click interactions:', function() { @@ -1039,7 +1067,7 @@ describe('sankey tests', function() { nodes = document.getElementsByClassName('sankey-node'); node = nodes.item(nodeId); position = getNodeCoords(node); - var timeDelay = (arrangement === 'snap') ? 1000 : 0; // Wait for force simulation to finish + var timeDelay = (arrangement === 'snap') ? 2000 : 0; // Wait for force simulation to finish return drag(node, move[0], move[1], false, false, false, 10, false, timeDelay); }) .then(function() { @@ -1075,11 +1103,12 @@ describe('sankey tests', function() { it('should persist the position of every nodes after drag in attributes nodes.(x|y)', function(done) { mockCopy.data[0].arrangement = arrangement; - var move = [50, 50]; + var move = [50, -50]; var nodes; var node; var x, x1; var y, y1; + var precision = 3; Plotly.newPlot(gd, mockCopy) .then(function() { @@ -1106,13 +1135,13 @@ describe('sankey tests', function() { x1 = gd._fullData[0].node.x.slice(); y1 = gd._fullData[0].node.y.slice(); if(arrangement === 'freeform') expect(x1[nodeId]).not.toBeCloseTo(x[nodeId], 2, 'node ' + nodeId + ' has not changed x position'); - expect(y1[nodeId]).not.toBeCloseTo(y[nodeId], 2, 'node ' + nodeId + ' has not changed y position'); + expect(y1[nodeId]).not.toBeCloseTo(y[nodeId], precision, 'node ' + nodeId + ' has not changed y position'); // All nodes should have same x, y values after drag for(var i = 0; i < x.length; i++) { if(i === nodeId) continue; // except the one was just dragged - if(arrangement === 'freeform') expect(x[i]).toBeCloseTo(x[i], 3, 'node ' + i + ' has changed x position'); - expect(y[i]).toBeCloseTo(y[i], 3, 'node ' + i + ' has changed y position'); + if(arrangement === 'freeform') expect(x1[i]).toBeCloseTo(x[i], 3, 'node ' + i + ' has changed x position'); + expect(y1[i]).toBeCloseTo(y[i], precision, 'node ' + i + ' has changed y position'); } return true; }) @@ -1121,8 +1150,8 @@ describe('sankey tests', function() { }); }); }); - }); + it('emits a warning if node.pad is too large', function(done) { var gd = createGraphDiv(); var mockCopy = Lib.extendDeep({}, mock); From 8ddfe7ed540106133241d4871fcf0009b92c7f19 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Thu, 7 Mar 2019 19:39:39 -0500 Subject: [PATCH 5/7] sankey: `node.(x|y)` honors uirevision --- src/plot_api/plot_api.js | 1 + src/traces/sankey/render.js | 4 +- test/jasmine/tests/sankey_test.js | 75 +++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index c8e440b32fc..3671d433689 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -2524,6 +2524,7 @@ var traceUIControlPatterns = [ // "visible" includes trace.transforms[i].styles[j].value.visible {pattern: /(^|value\.)visible$/, attr: 'legend.uirevision'}, {pattern: /^dimensions\[\d+\]\.constraintrange/}, + {pattern: /^node\.(x|y)/}, // for Sankey nodes // below this you must be in editable: true mode // TODO: I still put name and title with `trace.uirevision` diff --git a/src/traces/sankey/render.js b/src/traces/sankey/render.js index cbb37af9b16..17a8fc72a68 100644 --- a/src/traces/sankey/render.js +++ b/src/traces/sankey/render.js @@ -208,7 +208,7 @@ function sankeyModel(layout, d, traceIndex) { var columns = []; var colNumber = -1; var colX; // Position of column - var lastX = -nodeThickness; // Position of last node + var lastX = -Infinity; // Position of last node var dx; for(i = 0; i < orderedNodes.length; i++) { var node = nodes[orderedNodes[i].index]; @@ -751,7 +751,7 @@ function persistFinalNodePositions(d, gd) { x.push(nodeX / d.figure.width); y.push(nodeY / d.figure.height); } - Plotly.restyle(gd, { + Plotly._guiRestyle(gd, { 'node.x': [x], 'node.y': [y] }, d.trace.index) diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index 1ddf9a39821..7f36894170c 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -22,6 +22,7 @@ var defaultColors = require('@src/components/color/attributes').defaults; var drag = require('../assets/drag'); var checkOverlap = require('../assets/check_overlap'); +var delay = require('../assets/delay'); describe('sankey tests', function() { @@ -1150,6 +1151,80 @@ describe('sankey tests', function() { }); }); }); + + describe('in relation to uirevision', function() { + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(function() { + Plotly.purge(gd); + destroyGraphDiv(); + }); + + ['0', '1'].forEach(function(finalUIRevision) { + it('on Plotly.react, it preserves the position of nodes depending on layout.uirevision', function(done) { + var nodes, node, positionBeforeDrag, positionAfterDrag; + var move = [-50, 100]; + var uirevisions = ['0', finalUIRevision]; + + // Use a freeform arrangement + var mockCircularFreeform = Lib.extendDeep({}, mockCircular); + mockCircularFreeform.data[0].arrangement = 'freeform'; + + var mockCopy = Lib.extendDeep({}, mockCircularFreeform); + mockCopy.layout.uirevision = uirevisions[0]; + + Plotly.plot(gd, mockCopy) + .then(function() { + // move a node around + nodes = document.getElementsByClassName('sankey-node'); + node = Array.prototype.slice.call(nodes).find(function(n) { return n.textContent === '0';}); + positionBeforeDrag = getNodeCoords(node); + positionBeforeDrag = [positionBeforeDrag.x, positionBeforeDrag.y]; + positionAfterDrag = [positionBeforeDrag[0] + move[0], positionBeforeDrag[1] + move[1]]; + return drag(node, move[0], move[1], false, false, false, 10, false, 1000); + }) + .then(function() { + // Check that the node was really moved + nodes = document.getElementsByClassName('sankey-node'); + node = Array.prototype.slice.call(nodes).find(function(n) { return n.textContent === '0';}); + var newPosition = getNodeCoords(node); + expect(newPosition.x).toBeCloseTo(positionAfterDrag[0], 2, 'final x position is off'); + expect(newPosition.y).toBeCloseTo(positionAfterDrag[1], 2, 'final y position is off'); + + // Change color of nodes + var mockCopy = Lib.extendDeep({}, mockCircularFreeform); + mockCopy.data[0].node.color = 'orange'; + mockCopy.layout.uirevision = uirevisions[1]; + return Plotly.react(gd, mockCopy); + }) + .then(delay(1000)) + .then(function() { + nodes = document.getElementsByClassName('sankey-node'); + node = Array.prototype.slice.call(nodes).find(function(n) { return n.textContent === '0';}); + var newPosition = getNodeCoords(node); + + var pos, msg; + if(uirevisions[0] === uirevisions[1]) { + // If uirevision is the same, the node should stay where it is + pos = positionAfterDrag; + msg = 'should stay the same because uirevision did not change'; + } else { + // If uirevision changed, the node should go back to its default position + pos = positionBeforeDrag; + msg = 'should go back to its default because uirevision changed'; + } + expect(newPosition.x).toBeCloseTo(pos[0], 2, 'x position ' + msg); + expect(newPosition.y).toBeCloseTo(pos[1], 2, 'y position ' + msg); + }) + .catch(failTest) + .then(done); + }); + }); + }); }); it('emits a warning if node.pad is too large', function(done) { From 40fb223fe48cacb155f294193fedbffc15a93069 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Wed, 20 Mar 2019 12:55:28 -0400 Subject: [PATCH 6/7] sankey: call _guiRestyle via Registry + slight code simplification --- src/traces/sankey/render.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/traces/sankey/render.js b/src/traces/sankey/render.js index 17a8fc72a68..2c98e761ccf 100644 --- a/src/traces/sankey/render.js +++ b/src/traces/sankey/render.js @@ -23,7 +23,7 @@ var repeat = gup.repeat; var unwrap = gup.unwrap; var interpolateNumber = require('d3-interpolate').interpolateNumber; -var Plotly = require('../../plot_api/plot_api'); +var Registry = require('../../registry'); // view models @@ -233,7 +233,7 @@ function sankeyModel(layout, d, traceIndex) { } // Force node position - if(trace.node.x.length !== 0 && trace.node.y.length !== 0) { + if(trace.node.x.length && trace.node.y.length) { for(i = 0; i < Math.min(trace.node.x.length, trace.node.y.length, graph.nodes.length); i++) { if(trace.node.x[i] && trace.node.y[i]) { var pos = [trace.node.x[i] * width, trace.node.y[i] * height]; @@ -663,6 +663,7 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks, gd) { }) .on('dragend', function(d) { + if(d.arrangement === 'fixed') return; d.interactionState.dragInProgress = false; for(var i = 0; i < d.node.childrenNodes.length; i++) { d.node.childrenNodes[i].x = d.node.x; @@ -751,7 +752,7 @@ function persistFinalNodePositions(d, gd) { x.push(nodeX / d.figure.width); y.push(nodeY / d.figure.height); } - Plotly._guiRestyle(gd, { + Registry.call('_guiRestyle', gd, { 'node.x': [x], 'node.y': [y] }, d.trace.index) @@ -814,7 +815,7 @@ module.exports = function(gd, svg, calcData, layout, callbacks) { }); // To prevent animation on dragging - var dragcover = gd.querySelector('.dragcover'); + var dragcover = gd._fullLayout._dragCover; var styledData = calcData .filter(function(d) {return unwrap(d).trace.visible;}) From 65c7201e709765f1ab57e331bc00fe5a003a65c8 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Thu, 21 Mar 2019 14:32:36 -0400 Subject: [PATCH 7/7] sankey: arangement defaults to `freeform` if node.(x|y) is specified --- src/traces/sankey/defaults.js | 7 ++++++- test/jasmine/tests/sankey_test.js | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/traces/sankey/defaults.js b/src/traces/sankey/defaults.js index fa31a1596c6..a82706c3cb4 100644 --- a/src/traces/sankey/defaults.js +++ b/src/traces/sankey/defaults.js @@ -84,7 +84,12 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('orientation'); coerce('valueformat'); coerce('valuesuffix'); - coerce('arrangement'); + + var dfltArrangement; + if(nodeOut.x.length && nodeOut.y.length) { + dfltArrangement = 'freeform'; + } + coerce('arrangement', dfltArrangement); Lib.coerceFont(coerce, 'textfont', Lib.extendFlat({}, layout.font)); diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index 7f36894170c..9fd99be2789 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -250,6 +250,32 @@ describe('sankey tests', function() { expect(Array.isArray(fullTrace.link.label)).toBe(true, 'must be an array'); expect(fullTrace.link.label).toEqual(['a', 'b'], 'an array of the supplied values'); }); + + it('defaults to `snap` arrangement', function() { + var fullTrace = _supply({ + link: { + source: [0], + target: [1], + value: [1] + } + }); + expect(fullTrace.arrangement).toBe('snap'); + }); + + it('defaults to `freeform` arrangement if node.(x|y) is specified', function() { + var fullTrace = _supply({ + node: { + x: [0, 0.5], + y: [0, 0.5] + }, + link: { + source: [0], + target: [1], + value: [1] + } + }); + expect(fullTrace.arrangement).toBe('freeform'); + }); }); describe('sankey calc', function() {