From b4d056685f45527d62e4222003287bdb9a04d262 Mon Sep 17 00:00:00 2001 From: Kanit Wongsuphasawat Date: Wed, 15 Nov 2023 17:47:04 -0800 Subject: [PATCH 1/5] docs(examples): add broken example for https://github.com/vega/vega-lite/issues/9177 --- .../bar_grouped_discrete_bandsize.vl.json | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 examples/specs/bar_grouped_discrete_bandsize.vl.json diff --git a/examples/specs/bar_grouped_discrete_bandsize.vl.json b/examples/specs/bar_grouped_discrete_bandsize.vl.json new file mode 100644 index 0000000000..f18a359829 --- /dev/null +++ b/examples/specs/bar_grouped_discrete_bandsize.vl.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "data": { + "values": [ + {"category":"A", "group": "x", "value":0.1}, + {"category":"A", "group": "y", "value":0.6}, + {"category":"A", "group": "z", "value":0.9}, + {"category":"B", "group": "x", "value":0.7}, + {"category":"B", "group": "y", "value":0.2}, + {"category":"B", "group": "z", "value":1.1}, + {"category":"C", "group": "x", "value":0.6}, + {"category":"C", "group": "y", "value":0.1}, + {"category":"C", "group": "z", "value":0.2} + ] + }, + "mark": "bar", + "encoding": { + "x": {"field": "category"}, + "y": {"field": "value", "type": "quantitative"}, + "xOffset": {"field": "group"}, + "color": {"field": "group"} + }, + "config": { + "bar": {"discreteBandSize": {"band": 0.5}} + } +} From efacdd01524aa9dcfb71aab029f82603f615e843 Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot Date: Thu, 16 Nov 2023 01:49:28 +0000 Subject: [PATCH 2/5] chore: update examples [CI] --- .../bar_grouped_discrete_bandsize.png | Bin 0 -> 8004 bytes .../bar_grouped_discrete_bandsize.svg | 1 + .../bar_grouped_discrete_bandsize.vg.json | 143 ++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 examples/compiled/bar_grouped_discrete_bandsize.png create mode 100644 examples/compiled/bar_grouped_discrete_bandsize.svg create mode 100644 examples/compiled/bar_grouped_discrete_bandsize.vg.json diff --git a/examples/compiled/bar_grouped_discrete_bandsize.png b/examples/compiled/bar_grouped_discrete_bandsize.png new file mode 100644 index 0000000000000000000000000000000000000000..4d3dc9cec32af2a6f02f638bf317334871ff3516 GIT binary patch literal 8004 zcmbVx2RN4P|Mz7kC2lDqy51TMi6T*NJ0l&T$3O);@^4 zMT_A-_RieQ3>!o%HY~-vG^rr|;;Kkyw#ca$FA`{}qn_(xLvD~i%`q{PRMS+AO(f%V zIu$>=`zY_4u`k(8V-E{D_lAC_g3XVXEBh~O7doe!$3AlgQW0V>y))tKqXXSZKe_FP*w`cer9_-i!9FKQe71itOE7aqC8~f>9Pmjhp@7z-< zlEt_8XZc+hb$u$ONO@E~RDU{6T>T_AHe=v$wYBx$_Ec1r(=>TgQ`46Wc}nM% zF-9pVsqKvcV*=y|B~zeu8t(hMSOVns?b`|F2rig{;iW1kziAUOeBj={Cx^iuAFO|m zWALx#gi-MlJ|bLPT*VIGq|bA3jJpLU#KaJ0s)zRF=`^>tlBA`j4VGKeT6{_hbsP7g zZI5JZju$X@Sm@{NlxS;f<4xHeEHRIl^pi+WPj}en*foIVv_at7V|y$ zZP_V79+cjCUnQQO;IrhRSX(4xSh@88T0j6rK|#?;dP%+*bLUBTxL#yYintfnCts3q zZF#f?@lU*JKUP^-QE?hk)zLXpt~WaGc76@)FsrLIQ6(h; z^@j&uH(@!j@|O9&yw&fqDw7k90mUUHQY&M1C3oIYYwGBje`^Y&lknd5iTc%ajPD#Q zcXf4@P6$h5W@e_h7{`h)6(J#^ZTMpoJG;oLD&g0C*udamLS%1m&!ItS=9RECH#f&Fq^ztwSY{R5?;~gu+A*=WIbt6Y5z)FdQu%FWCfIGv%6umcQzJy`wgp| zkLKj$yw1sq?juJ<8{IfMNWpOBoUkxmQQaneB(vn0&8TInPcW^qH)`Db7e$Ka`~^*I z?f3T{h1}qKMQnq?5WgucMNv_GvAN~qBBY(K`=I#|<C>t$8?mzR}cw6x+ZYTmq|?(gqUSnBQ7e3h9gG*ooy;;@pfe9m3QL&=Pa zmzDk->+3D8VYDoP{?JR%!e6pgm^*j5;>fw;a=RqDy1ERkQkcjZx~}^Tf1oS~4?kYJ z_MUrt`nmB-?K}bRZHJb&HnR!b16$vV7w#8NPfy=aQIS_rIO^o&bUAEz?Ct#{(9|Rj zE76xEB_(BDC=3>x5@p>=WXG>3B2DJeDGI{4L=-R4 zE+Y45J~r;IG( zo=8|LNsOq~8R#ZV3>4yPjoa1E7Y;Ux{H!X*eYQp|m}mV|0k-Bf(P(tBRS!GjuU8;v zW5WZ};F&f^3SdiDZf$NR68Buk{piV7)6@)PkqMqid&Csu^k-TkJ=rSlu+OTxx*3Mo#p>nBB?(yS;Cth-~$>xcW@^pzS zW>OOn5J1O9S7@@}{NZ)4)6xi_{s&7fqk4b+yhsRhd-LYa#PoEKfO*?X`|*19&#fpP z)S09s9?~BGHlFht+cn!Iw8PJ$&%;vC3c|A2F{B*!F|G@Z)h)gpZ)2Z1661*mHla7C zrM1;`e`f>n@9tJ7V|fxC%`$E9X9EEJsZO5`4h|+2+g&ULKn^au@A%k1L?pA}aDRnP z*x?+Ec$FLgfHChpIW-j>6Z7!<_d7YWrZ{S~Y^NXHY&?2pWKenhmoCM6Uc8_|j4h`R zJI&kVPe4RPLlanATf5s2Tkb}8`5qPNaXSCdlN{-_wFYvc7z}-?-@&!zvAQrFBM%aw zlX!8jin99(M+a?3Dp*8C8D6}2f#(#9&V;^KS=H6__!4?Rbi`qE@wTq6?nD@0{S&Ct z=(gc<>*(0nfR+}_d3JU*JGB6uY6`ST-JuUQ8a>XyfM6$m(M0XFSco$;wpP zjRH|U0x-h!^SgIPXFny&09q_AE;_8wsjdI$k-vXDPLU)|v2U&lu;=Ic`qQpAQZ5iru(~jL(W5~p%j|_LpuFw`F{ug zw64jym0Yj;T5I%4ePoO%OSdE`PkBxzLljnvwqZy1T{6`V=4`T%|GTFFX8b)cy)WZU zTwGj8Xy^mIg0}6|sp#nF2S7xp4QpKEMwwC3jaV1rJncLb9bHJ!F-8tu!hvq(lRhG@SagA@H`wv>28vQ4?QqIP--gk ziL=7~cRxHEty~_lZ)gS5i!^NxZX6z__wevId=zyZ(<@Ofa%2G*`r$*uBry-_zP`S} zY8UPk%o5=^9L{1ZG&Ho4!M^sv>QuXV?`t(6fF@uK6D&42Cnqnjd9pRk5sJv&)3c&8 zn+1)P*JEsySZz7^b91xpxv_63a2K%4sjRFls9<~7)cky&?VTM$B)_1*)ZD!1d+Ooa zw{MGqIHjZr4t{_3L-X=35lE7w)ONeExP)A2_qFOld_3LY(2(iO#}^@CVP5`GX2O`eCE9+k&13=H}~yf>r=%Q!JJoer+>YOkiqi zium{T_U=Yv?>jk_Eoam@h5H!fRW~UNN6g3RrBsR=iu!DHrxfNG&WZ61KFsPHA#!W%#6LWQewS+r~X}8TQq-^%HuXXq|0%Z*E~FdO*={ zxQEi+&`QW8EG#TKC1vcpi=9Ta9t~@EeJ61MhwZBy&VnRcPYqW{3h>4vA&_$N_XCzr!iR^1vNkMrh zjArW;840^C(nT_fKW^BY=A*k}Lx(LMLKjShuD-r?MO|IpYUkB)6X=DxfgCxQ;$%Bqb`*f9^T1<& zW58vI0~*%Kl~`Y2KMd&A!Yx0yd(+heo&G6j$Uh=H9CPQ+tBYPNfYxPtVF*%aRQLGn zSA*Vx0ivp_R~M$HSPKNuXqjv+T=1hutXHm7bXfx&05(K-(}27CGVc`Bu(x*1%-sCj zw{HaSlH+HEfn86#Z-0B_()9-{giQF1YRMQF81i*X2m?qdq`@$NEN%vL!^-9n97ABT zbNvMjpHd~G?$3U*VtUSoPM^Uy@BI8@pbTK4wAZi6_IJ0AAY#8hUg}O{MGM|3AkA-R zkgTv7B0O^BNWOmM32-mLMMZo-otgh&P$Ezy0TJm-{Ytx#$B(g&j363zinM1<{C0kD ztS<~u`uh6nHu#EjadW$L(ZW7i3z_Wi?}McFht^~j6r?UJEZiO$KL|cXdvPvZ5{(Y% z!{SabiqObMo^1q^hhk);ke8R=+Z{h_1J-l?v*7OeE4<};5!^b36Km5S@O-eC5)vXp zTu?7)0Y}Qo!$a&~{~qrQ06CtIokc~n^vrXc=jP=JSoiZ_c{0G?A6Xo$W2kXkX?sX=ETO=#9D532gd>9j>_ZH7@w<2L5=h?q`1q*l={=sD zoMd;Qy=ZXV(^G_9If?V1L09d*;$$erUkxpHWgL0@_;EsSXR0JKv+s5qUO}%bzdxHF z6hwl>VlR(?Ln|&WzNcAgJ#bNM{fmq$S6oMDr>w0lZ=rF+2^}4s|5OPg)35I)SEOnU zwYB$<_-r&;%T}LfGZ7aPa{#5by}O$Mq8!M>!O01$sR42U=m7Q@`krv0(6CwZV3iV& zg~&_w9QdMNItfRrqM~w!cR}+HEcMr3kr)fOmZKF{f57BXu%3PC;N=tW1h&JM@l^+X zs#{`4A}uYA_msK0Ts(%=Gzb+FQ{-RTZ1MwN0|rn*=$j&@7j@%IqXbkiCl^-}FxCk^ zK0fcwAxmUALN7KpHnBG`F|oP5U0#kGwx5;FgkS=UQ5!tq>s-0rXf+EPTc(07JgdZI zLHl+ASUVEtxc@a=`L_=K-=xI9M&f@~yq>%;r^{2=R& zpAjSm2Uz2}6!VYI1@}LNdq5=Y^qq{!XI<@Mf>55qEf0@tq?AnIfq}%{tF5%quuV|L zcgtj;bkzx6Av}o(=+Dp3=l9yQfwUzfH#b);qNYX^AFnWpdzyf6bpIYL|0FJs?Js>x zUEFYZASxy%h9K`(yG(C?)iP>lHc#J~72gB%(jFjnV9%g%l%UKSd96x1v<1XRie zjjRcfwZU*U;jSJ2eTP}2Uu)m_AB_?H;hn1!`{wbvec?rU3HckF*@6Dn5gYC zz!Qws8i+2^(%10ae4*_Ubg}TSM_++Jz-x0+%E5sjwDFfxOBG03(m~Q&eokq9!Xkqp z_VvHnd$B?i+Yiop#FJI_%mDk*{VA!5z&x@HHQUuyZ85CM%J1uS}X`19D9(OR{=LR9-{G&TlX74@-ziZx_|HIqJZMUH>2UZ@*L%HU0#e&%u6l9y?I(TH|gHm1d5EZ+)`bGb8k=X5dD8h z<$t>+w5-(#(wNm|N=Y2}hz$9tdiw}KVo2Cj!ApPnqFPm5{T>U^%`CX0&p>svDPAW) zJ#NosL>0R(4e$2Xt+p|2FW0Q78XHGNv&v5b$_hdn(|zm$>mcNKX93Z-7mN=d7Z>v% zr@u@Fp+!JRNlDfWxF@`4@jcvk1M!nqTuhaC#kMKYzWzc68ycM)EBLVUEjj@+Vg&yXAOXAt>D^oVz^7Z~z@n}W0W}nDW1(S9vF*r}e=wV# zaR5}6mX?<7=tb0z>kYp9e`UOB8tOA9z#)$Fy%)mi1PCFB#N55hc9xNmq0imRixr$P zgpENjUYt=*5etnWV>j}VR&-jcB zhpxcekMY%>%Oe42{AgLp6py-RldI3`uX=SgB>-b(*4BPAuJL9IK=#l^+J!2t(CI!%*H&DfX{ z2o|8^);Mnk1H_?i06Z(ZDFEf5K69rcZ|EnX@5um#6qlE`LPH&gOd?WOAFLUU#a&%l=5TmFoK2rq; z#L34eZ)9YY;X*ezW+n-N0OB7LL*3~|go7-1qffi03?f4agv|hter|!<#QWEkhC| z?oFm|$}o$2Jv%uG5M zI6PiTld-f#G4mlIU{H_I($eA|i-cGY6ox|5D&V<(kKj7MFgciOu)$nc?8kTlsV4Wg z#%U1@Y(-Fcd3kYVB`vf>Idbk9uRePq6L)RgP1{NQqf1IgEfZBlOpO>y$a83&IG6U@(|K-BODP zG75@QNDCMQxIf84&UtPv^MVa&jpNn{RL|)pUIgMGMj&G0gbWEEsRsep7nhcv#Fp!w zEar++gbR-_Sd^NE1}ng1P-$stmDg4wI0{;%WvJBB#g8SY=i6DH@*89il%8K~?C2mf zZwn{Db1ey|FL@wzxO3t6JMd1rc3-YE7_t|s()JqnRWvdIw-b=NKT^-Z=<3qp!?fx5 zrw|<6DFAN}xVgE3d9d27bN)e%;6U!l$;r+2WK+U8xbu1V=8fPjLnw)A=O2XT=H{Gn zinW`=)*YWew^!JX6x)wWhyeuB@ta&9G2%?$HNmNeK@bQL8|441sfT+3pe{gVN^5DILbI}t%&$?E z-yD~@hdqpmiGebYL(V}Y8l;{>gD+7lE32lCjvJ7{ggQ>O9fi=8;5yuA>5#Fs0Q8X{ zrf_EfN)zMc^akw8De#TbX{sS$KFMIi2(AMH%fQfA3bDk9DR>$DlarH^k#lQnYZw`s z01L%N&_}Ra0FZd#rJ8KGU%b#?V7+!`=2J2^LI%!SO-n1YCQLq=egF+HKubIIB~7N| z!-s&pJZ`-Gu(gd?Uv~y{!9k4gv$Ng^3QfRyPL-I1gaCmhT^=_8V@?V?g}*C1JlL=D z+~DHjV;6qiAXh27*oHU3(v)>*H)a(PeLXnF+ z8wxQ90pbfj8s2!0pPve7A!28|!A}zAQVevPZ&;&UNgE}?3z;N$sYR!>lat`$(h?q# z=jXBB8+|(QS8Rt5f5!uGU|{p03kK6svrs z_r&$Ryf~+#8P}=0- Fe*tpb01E&B literal 0 HcmV?d00001 diff --git a/examples/compiled/bar_grouped_discrete_bandsize.svg b/examples/compiled/bar_grouped_discrete_bandsize.svg new file mode 100644 index 0000000000..17a4a4b964 --- /dev/null +++ b/examples/compiled/bar_grouped_discrete_bandsize.svg @@ -0,0 +1 @@ +ABCcategory0.00.20.40.60.81.0valuexyzgroup \ No newline at end of file diff --git a/examples/compiled/bar_grouped_discrete_bandsize.vg.json b/examples/compiled/bar_grouped_discrete_bandsize.vg.json new file mode 100644 index 0000000000..8e918a33d0 --- /dev/null +++ b/examples/compiled/bar_grouped_discrete_bandsize.vg.json @@ -0,0 +1,143 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "background": "white", + "padding": 5, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "values": [ + {"category": "A", "group": "x", "value": 0.1}, + {"category": "A", "group": "y", "value": 0.6}, + {"category": "A", "group": "z", "value": 0.9}, + {"category": "B", "group": "x", "value": 0.7}, + {"category": "B", "group": "y", "value": 0.2}, + {"category": "B", "group": "z", "value": 1.1}, + {"category": "C", "group": "x", "value": 0.6}, + {"category": "C", "group": "y", "value": 0.1}, + {"category": "C", "group": "z", "value": 0.2} + ] + }, + { + "name": "data_0", + "source": "source_0", + "transform": [ + { + "type": "stack", + "groupby": ["category", "group"], + "field": "value", + "sort": {"field": [], "order": []}, + "as": ["value_start", "value_end"], + "offset": "zero" + }, + { + "type": "filter", + "expr": "isValid(datum[\"value\"]) && isFinite(+datum[\"value\"])" + } + ] + } + ], + "signals": [ + { + "name": "x_step", + "update": "20 * bandspace(domain('xOffset').length, 0, 0) / (1-0.2)" + }, + { + "name": "width", + "update": "bandspace(domain('x').length, 0.2, 0.2) * x_step" + } + ], + "marks": [ + { + "name": "marks", + "type": "rect", + "style": ["bar"], + "from": {"data": "data_0"}, + "encode": { + "update": { + "fill": {"scale": "color", "field": "group"}, + "ariaRoleDescription": {"value": "bar"}, + "description": { + "signal": "\"category: \" + (isValid(datum[\"category\"]) ? datum[\"category\"] : \"\"+datum[\"category\"]) + \"; value: \" + (format(datum[\"value\"], \"\")) + \"; group: \" + (isValid(datum[\"group\"]) ? datum[\"group\"] : \"\"+datum[\"group\"])" + }, + "x": { + "scale": "x", + "field": "category", + "offset": {"scale": "xOffset", "field": "group"}, + "band": 0.25 + }, + "width": {"signal": "max(0.25, 0.5 * bandwidth('xOffset'))"}, + "y": {"scale": "y", "field": "value_end"}, + "y2": {"scale": "y", "field": "value_start"} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "band", + "domain": {"data": "data_0", "field": "category", "sort": true}, + "range": {"step": {"signal": "x_step"}}, + "paddingInner": 0.2, + "paddingOuter": 0.2 + }, + { + "name": "y", + "type": "linear", + "domain": {"data": "data_0", "fields": ["value_start", "value_end"]}, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + }, + { + "name": "xOffset", + "type": "band", + "domain": {"data": "data_0", "field": "group", "sort": true}, + "range": {"step": 20} + }, + { + "name": "color", + "type": "ordinal", + "domain": {"data": "data_0", "field": "group", "sort": true}, + "range": "category" + } + ], + "axes": [ + { + "scale": "y", + "orient": "left", + "gridScale": "x", + "grid": true, + "tickCount": {"signal": "ceil(height/40)"}, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "x", + "orient": "bottom", + "grid": false, + "title": "category", + "labelAlign": "right", + "labelAngle": 270, + "labelBaseline": "middle", + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "value", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ], + "legends": [{"fill": "color", "symbolType": "square", "title": "group"}] +} From 0d7ec05303328e07d947ea66ef8160989b9e7bff Mon Sep 17 00:00:00 2001 From: Kanit Wongsuphasawat Date: Wed, 15 Nov 2023 17:51:56 -0800 Subject: [PATCH 3/5] fix: apply bandPosition to offset channel for grouped bar (so band size works with grouped bars) fix #9177 --- src/compile/mark/encode/offset.ts | 2 +- src/compile/mark/encode/position-rect.ts | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/compile/mark/encode/offset.ts b/src/compile/mark/encode/offset.ts index 0ee51be1d2..b2da454a74 100644 --- a/src/compile/mark/encode/offset.ts +++ b/src/compile/mark/encode/offset.ts @@ -26,7 +26,7 @@ export function positionOffset({ markDef: MarkDef; encoding?: Encoding; model?: UnitModel; - bandPosition?: number; + bandPosition?: number | SignalRef; }): Offset { const channel = `${baseChannel}Offset` as | 'xOffset' diff --git a/src/compile/mark/encode/position-rect.ts b/src/compile/mark/encode/position-rect.ts index b2229b1433..873f06e693 100644 --- a/src/compile/mark/encode/position-rect.ts +++ b/src/compile/mark/encode/position-rect.ts @@ -203,7 +203,16 @@ function positionAndSize( const vgChannel = vgAlignedPositionChannel(channel, markDef, config, defaultBandAlign); const center = vgChannel === 'xc' || vgChannel === 'yc'; - const {offset, offsetType} = positionOffset({channel, markDef, encoding, model, bandPosition: center ? 0.5 : 0}); + + const bandPosition = center + ? 0.5 + : isSignalRef(bandSize) + ? {signal: `(1-${bandSize})/2`} + : isRelativeBandSize(bandSize) + ? (1 - bandSize.band) / 2 + : 0; + + const {offset, offsetType} = positionOffset({channel, markDef, encoding, model, bandPosition}); const posRef = ref.midPointRefWithPositionInvalidTest({ channel, @@ -215,15 +224,7 @@ function positionAndSize( stack, offset, defaultRef: pointPositionDefaultRef({model, defaultPos: 'mid', channel, scaleName, scale}), - bandPosition: center - ? offsetType === 'encoding' - ? 0 - : 0.5 - : isSignalRef(bandSize) - ? {signal: `(1-${bandSize})/2`} - : isRelativeBandSize(bandSize) - ? (1 - bandSize.band) / 2 - : 0 + bandPosition: offsetType === 'encoding' ? 0 : bandPosition }); if (vgSizeChannel) { From 4deb3638472c5fed6a0526e000b7e0650ef7e5ee Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot Date: Thu, 16 Nov 2023 01:54:22 +0000 Subject: [PATCH 4/5] chore: update examples [CI] --- .../bar_grouped_discrete_bandsize.png | Bin 8004 -> 8011 bytes .../bar_grouped_discrete_bandsize.svg | 2 +- .../bar_grouped_discrete_bandsize.vg.json | 3 +-- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/compiled/bar_grouped_discrete_bandsize.png b/examples/compiled/bar_grouped_discrete_bandsize.png index 4d3dc9cec32af2a6f02f638bf317334871ff3516..d46950f8caeb01dcbf05e91cca620987f90702c7 100644 GIT binary patch literal 8011 zcmb_>1yodfyZ5FMX^;*X!~m60q$C~6KtK)>0s|P7ggA5zQiGI~qST>5LO2rALkk9i zlpw9rrP2cT+2_3HJ@38iuJx^NeV@7noz33+|I{y@FkKx@209)(1VI?IwXpgKLS_Wt zFVj-P-<7>@<=_jo)g?_VvXB1$y)HKnK?D(Ptg3-W^5Tf6!Eayplvg$^|5FScQFMoW-rIttFkuReUQ|`5)a|tRD>OM(#by_ll$2C& zYI8kB6xWra5b843He16mN4R&taNce|(EW(9vGKNJ`iRRz0#U#NkH>q-iNj6hQ%;#y zkUo9l*3r?~{1SH+=ior^>gtM+@~`Fou6uk1{%i8<6N`}GU@E`u*<&}V?ol;1HqP`F zaC)sT9Psz||Fu5sgb)I0nAFmBah>re5JE%*{h}m-YYt)1Jw@v|^|f?#WQ2>q zd@1|oevJAg3tp6)n>$g`n!5AN8x|IpfR>h)qndfz+S z{e{LJ%XJ&h3xkpo{0bbEjw3xw);2bSlU33D3L#VnIS!vc&lk!h*p@8kYErmt;97#k z!ap_4%=pcH*M2)`RD!&y{Z3a{Sjf}o>E&fL)fR~m-oCw@tmw^=nwn}UFJ2n~2X-<0OGF9TXXwuT&o>^aC{|Wbv7J?>15%_qjzU-Gmvp7SueTRL?CkB+yKPJqPivN7d-Ko#czZ@dN~#GypO~C{fSz=I zZ^Nd{X{>(b`*$@jFZof=X?`PPW1-Wh8Qt96JQhmZbqx$CgVh48+oLQumPR%gUUoe@ z|LuK>!o=)s*vH(fQ}8VIEw4pxKTeiOoA$b~hd9KOqn z*KcAbr>0nVdBg9ddQ$7@>782dWPzMPsr7CC{CL&U&hBAiBA3ls9!e@IGNiVyuDcf# zaQgIVVpy2%dg5}!zS5}wzVD*j&6_tfdAz;79fr!Zr~=Sel)#1MecXcEug>PCRSa+4exDrn^vs>nAAZS!&<0o&k~tKg_r_A| z+WdfNVa@$n*4Vf>Ju|aY1&1)0x<{-fyyqNLR8$ai|6M$vvS00|>MrGdKJmJrto6I= zrsvmsujS?CJ?ds;i80qYF!85&2{eTi>-Z9do6qR8+?{w1MigJz%#01a!1-72xo=jZ1i7Zp`AG<@u) zH~hyRQkRp?F_1IgR?u`Gm1FpAUMgA5YVn4umnad+tK zi3vI_y1|0$FECP4tfC2YxjHFL$I^UyZjMyk9Y&Du?(V@_&oiG#MsAf|!1j`2MJ_Q5 z3PzWeoyB6YS_;weCmu8g(pXQwPl+^_k&$VKbmxv3<{KE*`&3165Q)!zGcXAH{{3EW ze}6_!4z-5Hz1x?z@}Sh@T&Lf+(g^wxo>=&CJ2mW9l>Kln<;2YCpL6_7D@2)d`RRk| zYWciAyO!2g%eRS=cl}PIQ{_Z@dO9rw0~Nx>&0W4JyT3chPR0^*gs+iRFE?}fSlRtn zsN=8d0J(}P0QT6>3 zFg(iN!_=r98 zd&f>q!4GJ$Mxh4{3kz$6>LWv1TU(jc4xc)8<*$=uQ_|1~%gS=Ct*v=1RZUB{e!FZt zjRqO7?Z3Y>Ch4()gDWm6(4eF5YfD=j=ER8ua5Md`W;Cu}d$KPXmD#Ycva;f=tT1ag z^mAyTb~#R&1gQp6H;#=7%E`%H>G$2=-QbeCNlVSZ6Qmt?G`+0M0PQyu3!hc0rO#D? zXxZ=W?IoVM6IJG_ePHnJaD~vBGi?l0Pbc1p zH^ige-#=7tACr&}yt(NCQ(ah8^x{xe7UjayNR6a*8$A~n7p^BuTh?Wgva_?3r?puyr|TY<01AMpXlc<_IF3B@ z{PB$pfngXE8{0E82tY4ovh*lkz8{}$-@|7vHb=~&bim;bS2&GDO{aP@y12M-N#GE# zpEChNLncR#9cvyK;9Or{M@P=XhYxWu%Q7{iGb|eXhOD8*^7`he+an_)f`DK|JXXlc zS+27w6f=wawqk z4PoS`OSnj-si}$8)O=$TO4Jh1qAb+UZ|N^G1-!)JR>o}2#l*yJH29g-)OoC0oiwjS z3vH<80Ffxbu0Md;9Gjr~ynE|OcJ{4I`4BM{MdGW$gMarkG#WM*K!kY2lJr*}|2OUg zsS@*R!*6*^Lw@dETlJsmxfn)v78X1lRpbcpgyIxNh8PKE#D z-TrwjHdphOn5Zc8p+i)W8i5DcjzR-5bSZA|Rl6^ru2IHh=oIv^6l!D!q@@XBFqjgj zF{Q-BMBH3&J~2GpK6;NxlrJ)*ScgtV%djn4kO^^ia|?-%X6otbG5Ngwr6bO$)E!3on$3n zo{u?K0xK(J82Mxgx86LKHr^O{F~DS>!5Hw|-h z0iD$IVE~g3Bh`lhCPUKFEMD$l)zv3}cC-W?U|V^6ri(N(!Vl09P+D4AkyBB@5zZ;z z)}5(2@$FmxxwH^s9)&HQa;JsuDyg}d!oqj$5S#^t7%pC3;Uh<~KKV*XNsWK~N`;h^ zln~GiH|*WHbLT>~zL2;$)Bdk%WvK3ZX@bGTJZoEN9Mt2ZM~?{HXKv%ZypBmcYs(6d zin}xLt8=Z=Y0S|%G~i-(6OatvUUm+S$+k#d;=_jz%FCs1zk3!8vEJD{zI{xT4!?bY zslxPYpZsn*O`1-n?Wf^kYd}PK!QP=E9+YW1K8cSfwYBA_Mn*<5*xA{YxJ+rl3pc~Z zDBEy$mOu{6$yIge-35?+?^`BQGt-WL{wA$(*v2e>%2T07y8F5~|E$To{p~CreA?UZ zF`p02nii1NcyBlsnv_59o@YCHnKaoNp7i-n3z21WYdS66uplNbE^vF>Yj9}jT;A-* zT&9z!PU)GN9<;W$Hn@JhxupfWQ*TlfVp8N-auq06XzGii-cp@`uHWZh{2H2mHp9WQ zy}i+F-?xg-3Q+NeU;*ohdG>5*2Jhswe0$@mg8ON5a&lc0lZO{V87(_vMPP!dxVT6{ z@i^~p=f8EOMeAj1Of&@_Bt}PTrYZR@ZTw(n z!E+T+7lN=jdGci0uz-NTCP;}==mmN%iQhl%E-WredVc>x{Q^JV&j7g2VE$uuH6WU4 zI)gaV_|c9epyb0fKk#C}@kv60kdTn7f~W{4Bd46F1ckc$JMoZgfa;?~ComZ_s_^Dq zTtt}oixv6#Cqn4C$rCP$luKeV-WT@~@b~WrWoBA?|5U^NRIy#xF20g#w{LV2lRXgKF(s>q@@(Ca_~VI*|&WTB$B5XS5GnKBMC zAsFO&1qBq&?vDK*KVCDj#$r=abkN{En3*`e$=YdgUi)^HE zWTehcIz_Laq^%#*E=!-~7N`EeVtZ>1r*SFC$&Zqf4gwajv$NyINdXAqPMWWwmn*!H zaRJ%<)Knm<|N8qm=KG79QHk>PYw)L+cPcgg;R?e7vLpaXfaY{aEG-k0lM6pkr|lPT zXaBveJCG=3bad3H!B0_CRCFj)946a~KEfL0>BS3ioiwG$k`f77Ik^cZ{~g}bH(q`C z_i%Vs% z)TwFFyN(W34ULdOLn85~+Vt|5YEBUm5n5VW!poOudOm(E_1pD|5Kye{7@C{2i{L&( zeKDIAK>c6pL3l7P@A&5SwiW2O!8&glj?*^=9TuPeRv_W`%Y*waGs|p3JmJda%e|*x zFGA=iC#_&!oqJy6JVCC1?V6-*&yl|`s&kp57Vw%8T6BA1UZ<<8`ytP<@d4H5OlI`W zG8Jr=J1~-WZ{O;gnH7x`7Z-;-eQF%|4D|CvdZ@qy{d}yO+c}tDXn#XO{wG^-WOI}6 zpWWt;zw1-G{`(7akD@%`Sk@A_&nu8wK0ZFW*RBOVctAclJS>x!UsR+Hh|tv3q-$;- zWj|0nA-KOubIxH9L39iH;19!k*xzhn7@Hl1ul5T`KBqLgE0=#Qp?+`oFWNx!5X1ot zH|vpV{Oh-GTOJ+~W9H(by>{)IfuW%l@Nb0h=n*qY(3F$}P`KFMSbo899TFB0&jNy2 zDIDIXcD0Qc<-F5r{*2>GdD4=SrFwPH;2?-^m8oH~Wc0~75OdG3hibofiZPha1G$Qg zjpgZEUmg=Qt#nN2qXRLxqGt?p;2&1-503wb_42>bhyVW<#&DDf`u~!IfJ@-#4C3SC z&w4dvWl{e4_O7|aviWzzf>Qf|)5b+mHNo+O5hy1V!$Gx=cWVl^f)?QIi+}vsYUV>a z@Q7xRPGKu6&dJLD=6|YHArYMW!eBs^>vYrS&wQ@m-l_rx7ZnrZ?S1v?6%K3`)YYM8 z5{S?g@T3GW@wfinX&3Y-_4OS>a5ryKf|>^=dd}2;s*NElCub5Kobacl=IW50Nl|de z-TXOrftQ;*JtrrpV+iQwl(CssU~a%E}ELE9`3MOQAXdtdHUGI*>bASy^X?j){pCEb=-E|7XeC zi@PcRx{_OMf4dp~-22gs-%PLh?N#aEx1w}n6+Bl!BM=A(1c-)=j7&>gTXlJ~&hpl+ zV9=1L4Fr%+fz+<`n|`o4ySdWL#LEwg8x`Fir$F|CDwi^GWJ@S|f)L)P3FSscyZh_R z8D!gw*BUPNU9#S;HsqfTHT8~>qEWE#KY63xhdcWGcUn~F`K*7sgAj%*ouQ~@NEB_r9ftYMu+arE3tI%PG-XV|G(tI{}eNJ&Ptr$ z%x&~k%G~y#>BXg>JkMvm`&V=21I;N!+|@RoU!rl?%yp6PYc2NEzIP1!u3&T}0@4`% z;|P5E@EYa+%d>~9#GgKWy1Cje2)rTy1ld{R%x;^KJ6!?*V26(%rv;z#4=`rz-1Rj| zrn}PGe{?A`0WtFVwc2h}<90U=?^M5aw0?ij8-$zX&zWxU??V2HSSRgzxOlw^D3QJG zfd&ky*b5=_jlZ^jSlZa+6;JT8 zxy{4Fya>+PIuO*NFjT4PyWoSI<4a47Y;0@@35++FZ*RG|7yoL|QzuRYR8>{E4L3EB ztE;OQ^+Rg^(mkN(y8CH-w`X%p?fOI`c%KRrXEs;UYEku+N8UFtlc#>K-kxxP3I_9IqbpB0!XkhGhZpGr}|1Jy2H zrUw~{dO?3GWEy%d!tTz-h0N~5;4XsCiy)9HKtnrV8>XQ!-;XC;kC0ELj*Xn*_mZPX`n8zzwu@FX(|!7%#yFuL>N&&{dm`I=yzz=65Z6)!xa{t^c6 zAB7vf;^Z+{g_ag}I&Er$x z6tx1wfDolmFUdcaw(rNS&Gki^TYx33w6|%kcpqCm$*#&PCNAF2r2LD(ui!TRbN1tL zF|lR{25O_8GYU^HNCKbLZ)Z^k3T|Q|R8Yg%n7b{4I}nnIN*j53ld)57To8V(D_4Rq zX4|cJ!3E&JxlA-sDdIJXEJ+pV5DAn$Ten3tg0`EOo(}cf-4;?%FdZwx%c3X@t{@mg zmiG4HNit4q`34!q#f+$1=(hN*-gH4?dwntd%@c8%t<>=FgT>}`L+|-xie=S5gn*{P z!JG>f^h4i(ue5?RILOu3)&~4CFfNWQ{-Ov}?Q7z|;yxrMaLj+du6rJAlv?-Nz&JNS zhwDFwof?FMg((!=7Eo)|!NCEQ4ImJy=OsRjwEAI$g~<@8?IC|^bj-~c6fxR@cA%xB zYXYlhVrq&4K_#rx=6E3WK%~5Y4vW_Px!w~CgQXObl9JBO&Lpri)<*P0SKlivmu>Dn zleQnqk9#i!n4jkJ{Q$f^f`Ca6)KgPMg^;R{`B(xs9H5b@3bmK&HFv_dQVqLE{BuL% z-=Y5Zp5#Bl`p7#tJS~{%zZDqH)*Qmr!JA~o)z+TplXXTw5~JL+C5#Ov*MoybUE5u6#RkumpKRR`yT z44IpoW9H|N%*eoiH*N{`Iyy*dmg~a>6unev0|4DwAifeZGNEZ{X>OJqD0qKJzX0vj zQe;}`JlS#pz<3-4VWP6X5@06izHkufjZIA%ecmo%OG$_A)NkBKo*AvHfu>}J?&9PC zfeUuN8m$nxQ!Uofibpjfc|=gF!9X+tdY&C3-PGN!hcHJ)MOlH$w1V!Ng!DA^_ZucA zC55*OcwbI@_UxgO?=}&31hhgyi4v9%ki)RyIRT59;Dm$(pUNjF{LZbXVZBIT30FekeJ?sMIPLu6X@gi%g~P zMwPyIA?&5-5X{-L28G5iH6X?Z5UZ{4Uonb`W(c$I&iWzvH3>}(Jz_`U)|IMAPFVTz zgAHt?NeF5axc)|v&r^f*2t%@VZ+rgryLYYNx}$E<`Mu34uCq3}!{)~`j!NM2)#-xCmzI`L z`qJgUM=`$itP(V)mX?;vl`9OW`i9y%x1D+%MJbyS!g|_|Y}k#QcU$@#27oZRSUCo6 zLP$~)FEal2>6yI3Lad>oVc@NU`=2*}MXJIo4LZ^J$G0n}#SI?~RV!crw*3)K{h`j= zJt#CZ^N&AREiEnY;ysBbIm<$4@jeF*8~~bm17T)lWQ1fqgeH0zAI}bpsl#x}*3H$a z;Fm97B8Ne99t&cg1mBaK0BbT=-zmEx)S<28D-*BZu7<28Du{ zt-gQhASZxHgx|gln4d3}6nYpw4L|3bPhf}kr@2pTnPQov!=03!rJl=pv~0Z}qOJxv4l4E65vSc70dKmh9K^%t8B z*Ky51TMi6T*NJ0l&T$3O);@^4 zMT_A-_RieQ3>!o%HY~-vG^rr|;;Kkyw#ca$FA`{}qn_(xLvD~i%`q{PRMS+AO(f%V zIu$>=`zY_4u`k(8V-E{D_lAC_g3XVXEBh~O7doe!$3AlgQW0V>y))tKqXXSZKe_FP*w`cer9_-i!9FKQe71itOE7aqC8~f>9Pmjhp@7z-< zlEt_8XZc+hb$u$ONO@E~RDU{6T>T_AHe=v$wYBx$_Ec1r(=>TgQ`46Wc}nM% zF-9pVsqKvcV*=y|B~zeu8t(hMSOVns?b`|F2rig{;iW1kziAUOeBj={Cx^iuAFO|m zWALx#gi-MlJ|bLPT*VIGq|bA3jJpLU#KaJ0s)zRF=`^>tlBA`j4VGKeT6{_hbsP7g zZI5JZju$X@Sm@{NlxS;f<4xHeEHRIl^pi+WPj}en*foIVv_at7V|y$ zZP_V79+cjCUnQQO;IrhRSX(4xSh@88T0j6rK|#?;dP%+*bLUBTxL#yYintfnCts3q zZF#f?@lU*JKUP^-QE?hk)zLXpt~WaGc76@)FsrLIQ6(h; z^@j&uH(@!j@|O9&yw&fqDw7k90mUUHQY&M1C3oIYYwGBje`^Y&lknd5iTc%ajPD#Q zcXf4@P6$h5W@e_h7{`h)6(J#^ZTMpoJG;oLD&g0C*udamLS%1m&!ItS=9RECH#f&Fq^ztwSY{R5?;~gu+A*=WIbt6Y5z)FdQu%FWCfIGv%6umcQzJy`wgp| zkLKj$yw1sq?juJ<8{IfMNWpOBoUkxmQQaneB(vn0&8TInPcW^qH)`Db7e$Ka`~^*I z?f3T{h1}qKMQnq?5WgucMNv_GvAN~qBBY(K`=I#|<C>t$8?mzR}cw6x+ZYTmq|?(gqUSnBQ7e3h9gG*ooy;;@pfe9m3QL&=Pa zmzDk->+3D8VYDoP{?JR%!e6pgm^*j5;>fw;a=RqDy1ERkQkcjZx~}^Tf1oS~4?kYJ z_MUrt`nmB-?K}bRZHJb&HnR!b16$vV7w#8NPfy=aQIS_rIO^o&bUAEz?Ct#{(9|Rj zE76xEB_(BDC=3>x5@p>=WXG>3B2DJeDGI{4L=-R4 zE+Y45J~r;IG( zo=8|LNsOq~8R#ZV3>4yPjoa1E7Y;Ux{H!X*eYQp|m}mV|0k-Bf(P(tBRS!GjuU8;v zW5WZ};F&f^3SdiDZf$NR68Buk{piV7)6@)PkqMqid&Csu^k-TkJ=rSlu+OTxx*3Mo#p>nBB?(yS;Cth-~$>xcW@^pzS zW>OOn5J1O9S7@@}{NZ)4)6xi_{s&7fqk4b+yhsRhd-LYa#PoEKfO*?X`|*19&#fpP z)S09s9?~BGHlFht+cn!Iw8PJ$&%;vC3c|A2F{B*!F|G@Z)h)gpZ)2Z1661*mHla7C zrM1;`e`f>n@9tJ7V|fxC%`$E9X9EEJsZO5`4h|+2+g&ULKn^au@A%k1L?pA}aDRnP z*x?+Ec$FLgfHChpIW-j>6Z7!<_d7YWrZ{S~Y^NXHY&?2pWKenhmoCM6Uc8_|j4h`R zJI&kVPe4RPLlanATf5s2Tkb}8`5qPNaXSCdlN{-_wFYvc7z}-?-@&!zvAQrFBM%aw zlX!8jin99(M+a?3Dp*8C8D6}2f#(#9&V;^KS=H6__!4?Rbi`qE@wTq6?nD@0{S&Ct z=(gc<>*(0nfR+}_d3JU*JGB6uY6`ST-JuUQ8a>XyfM6$m(M0XFSco$;wpP zjRH|U0x-h!^SgIPXFny&09q_AE;_8wsjdI$k-vXDPLU)|v2U&lu;=Ic`qQpAQZ5iru(~jL(W5~p%j|_LpuFw`F{ug zw64jym0Yj;T5I%4ePoO%OSdE`PkBxzLljnvwqZy1T{6`V=4`T%|GTFFX8b)cy)WZU zTwGj8Xy^mIg0}6|sp#nF2S7xp4QpKEMwwC3jaV1rJncLb9bHJ!F-8tu!hvq(lRhG@SagA@H`wv>28vQ4?QqIP--gk ziL=7~cRxHEty~_lZ)gS5i!^NxZX6z__wevId=zyZ(<@Ofa%2G*`r$*uBry-_zP`S} zY8UPk%o5=^9L{1ZG&Ho4!M^sv>QuXV?`t(6fF@uK6D&42Cnqnjd9pRk5sJv&)3c&8 zn+1)P*JEsySZz7^b91xpxv_63a2K%4sjRFls9<~7)cky&?VTM$B)_1*)ZD!1d+Ooa zw{MGqIHjZr4t{_3L-X=35lE7w)ONeExP)A2_qFOld_3LY(2(iO#}^@CVP5`GX2O`eCE9+k&13=H}~yf>r=%Q!JJoer+>YOkiqi zium{T_U=Yv?>jk_Eoam@h5H!fRW~UNN6g3RrBsR=iu!DHrxfNG&WZ61KFsPHA#!W%#6LWQewS+r~X}8TQq-^%HuXXq|0%Z*E~FdO*={ zxQEi+&`QW8EG#TKC1vcpi=9Ta9t~@EeJ61MhwZBy&VnRcPYqW{3h>4vA&_$N_XCzr!iR^1vNkMrh zjArW;840^C(nT_fKW^BY=A*k}Lx(LMLKjShuD-r?MO|IpYUkB)6X=DxfgCxQ;$%Bqb`*f9^T1<& zW58vI0~*%Kl~`Y2KMd&A!Yx0yd(+heo&G6j$Uh=H9CPQ+tBYPNfYxPtVF*%aRQLGn zSA*Vx0ivp_R~M$HSPKNuXqjv+T=1hutXHm7bXfx&05(K-(}27CGVc`Bu(x*1%-sCj zw{HaSlH+HEfn86#Z-0B_()9-{giQF1YRMQF81i*X2m?qdq`@$NEN%vL!^-9n97ABT zbNvMjpHd~G?$3U*VtUSoPM^Uy@BI8@pbTK4wAZi6_IJ0AAY#8hUg}O{MGM|3AkA-R zkgTv7B0O^BNWOmM32-mLMMZo-otgh&P$Ezy0TJm-{Ytx#$B(g&j363zinM1<{C0kD ztS<~u`uh6nHu#EjadW$L(ZW7i3z_Wi?}McFht^~j6r?UJEZiO$KL|cXdvPvZ5{(Y% z!{SabiqObMo^1q^hhk);ke8R=+Z{h_1J-l?v*7OeE4<};5!^b36Km5S@O-eC5)vXp zTu?7)0Y}Qo!$a&~{~qrQ06CtIokc~n^vrXc=jP=JSoiZ_c{0G?A6Xo$W2kXkX?sX=ETO=#9D532gd>9j>_ZH7@w<2L5=h?q`1q*l={=sD zoMd;Qy=ZXV(^G_9If?V1L09d*;$$erUkxpHWgL0@_;EsSXR0JKv+s5qUO}%bzdxHF z6hwl>VlR(?Ln|&WzNcAgJ#bNM{fmq$S6oMDr>w0lZ=rF+2^}4s|5OPg)35I)SEOnU zwYB$<_-r&;%T}LfGZ7aPa{#5by}O$Mq8!M>!O01$sR42U=m7Q@`krv0(6CwZV3iV& zg~&_w9QdMNItfRrqM~w!cR}+HEcMr3kr)fOmZKF{f57BXu%3PC;N=tW1h&JM@l^+X zs#{`4A}uYA_msK0Ts(%=Gzb+FQ{-RTZ1MwN0|rn*=$j&@7j@%IqXbkiCl^-}FxCk^ zK0fcwAxmUALN7KpHnBG`F|oP5U0#kGwx5;FgkS=UQ5!tq>s-0rXf+EPTc(07JgdZI zLHl+ASUVEtxc@a=`L_=K-=xI9M&f@~yq>%;r^{2=R& zpAjSm2Uz2}6!VYI1@}LNdq5=Y^qq{!XI<@Mf>55qEf0@tq?AnIfq}%{tF5%quuV|L zcgtj;bkzx6Av}o(=+Dp3=l9yQfwUzfH#b);qNYX^AFnWpdzyf6bpIYL|0FJs?Js>x zUEFYZASxy%h9K`(yG(C?)iP>lHc#J~72gB%(jFjnV9%g%l%UKSd96x1v<1XRie zjjRcfwZU*U;jSJ2eTP}2Uu)m_AB_?H;hn1!`{wbvec?rU3HckF*@6Dn5gYC zz!Qws8i+2^(%10ae4*_Ubg}TSM_++Jz-x0+%E5sjwDFfxOBG03(m~Q&eokq9!Xkqp z_VvHnd$B?i+Yiop#FJI_%mDk*{VA!5z&x@HHQUuyZ85CM%J1uS}X`19D9(OR{=LR9-{G&TlX74@-ziZx_|HIqJZMUH>2UZ@*L%HU0#e&%u6l9y?I(TH|gHm1d5EZ+)`bGb8k=X5dD8h z<$t>+w5-(#(wNm|N=Y2}hz$9tdiw}KVo2Cj!ApPnqFPm5{T>U^%`CX0&p>svDPAW) zJ#NosL>0R(4e$2Xt+p|2FW0Q78XHGNv&v5b$_hdn(|zm$>mcNKX93Z-7mN=d7Z>v% zr@u@Fp+!JRNlDfWxF@`4@jcvk1M!nqTuhaC#kMKYzWzc68ycM)EBLVUEjj@+Vg&yXAOXAt>D^oVz^7Z~z@n}W0W}nDW1(S9vF*r}e=wV# zaR5}6mX?<7=tb0z>kYp9e`UOB8tOA9z#)$Fy%)mi1PCFB#N55hc9xNmq0imRixr$P zgpENjUYt=*5etnWV>j}VR&-jcB zhpxcekMY%>%Oe42{AgLp6py-RldI3`uX=SgB>-b(*4BPAuJL9IK=#l^+J!2t(CI!%*H&DfX{ z2o|8^);Mnk1H_?i06Z(ZDFEf5K69rcZ|EnX@5um#6qlE`LPH&gOd?WOAFLUU#a&%l=5TmFoK2rq; z#L34eZ)9YY;X*ezW+n-N0OB7LL*3~|go7-1qffi03?f4agv|hter|!<#QWEkhC| z?oFm|$}o$2Jv%uG5M zI6PiTld-f#G4mlIU{H_I($eA|i-cGY6ox|5D&V<(kKj7MFgciOu)$nc?8kTlsV4Wg z#%U1@Y(-Fcd3kYVB`vf>Idbk9uRePq6L)RgP1{NQqf1IgEfZBlOpO>y$a83&IG6U@(|K-BODP zG75@QNDCMQxIf84&UtPv^MVa&jpNn{RL|)pUIgMGMj&G0gbWEEsRsep7nhcv#Fp!w zEar++gbR-_Sd^NE1}ng1P-$stmDg4wI0{;%WvJBB#g8SY=i6DH@*89il%8K~?C2mf zZwn{Db1ey|FL@wzxO3t6JMd1rc3-YE7_t|s()JqnRWvdIw-b=NKT^-Z=<3qp!?fx5 zrw|<6DFAN}xVgE3d9d27bN)e%;6U!l$;r+2WK+U8xbu1V=8fPjLnw)A=O2XT=H{Gn zinW`=)*YWew^!JX6x)wWhyeuB@ta&9G2%?$HNmNeK@bQL8|441sfT+3pe{gVN^5DILbI}t%&$?E z-yD~@hdqpmiGebYL(V}Y8l;{>gD+7lE32lCjvJ7{ggQ>O9fi=8;5yuA>5#Fs0Q8X{ zrf_EfN)zMc^akw8De#TbX{sS$KFMIi2(AMH%fQfA3bDk9DR>$DlarH^k#lQnYZw`s z01L%N&_}Ra0FZd#rJ8KGU%b#?V7+!`=2J2^LI%!SO-n1YCQLq=egF+HKubIIB~7N| z!-s&pJZ`-Gu(gd?Uv~y{!9k4gv$Ng^3QfRyPL-I1gaCmhT^=_8V@?V?g}*C1JlL=D z+~DHjV;6qiAXh27*oHU3(v)>*H)a(PeLXnF+ z8wxQ90pbfj8s2!0pPve7A!28|!A}zAQVevPZ&;&UNgE}?3z;N$sYR!>lat`$(h?q# z=jXBB8+|(QS8Rt5f5!uGU|{p03kK6svrs z_r&$Ryf~+#8P}=0- Fe*tpb01E&B diff --git a/examples/compiled/bar_grouped_discrete_bandsize.svg b/examples/compiled/bar_grouped_discrete_bandsize.svg index 17a4a4b964..a32110aec9 100644 --- a/examples/compiled/bar_grouped_discrete_bandsize.svg +++ b/examples/compiled/bar_grouped_discrete_bandsize.svg @@ -1 +1 @@ -ABCcategory0.00.20.40.60.81.0valuexyzgroup \ No newline at end of file +ABCcategory0.00.20.40.60.81.0valuexyzgroup \ No newline at end of file diff --git a/examples/compiled/bar_grouped_discrete_bandsize.vg.json b/examples/compiled/bar_grouped_discrete_bandsize.vg.json index 8e918a33d0..76916e641c 100644 --- a/examples/compiled/bar_grouped_discrete_bandsize.vg.json +++ b/examples/compiled/bar_grouped_discrete_bandsize.vg.json @@ -64,8 +64,7 @@ "x": { "scale": "x", "field": "category", - "offset": {"scale": "xOffset", "field": "group"}, - "band": 0.25 + "offset": {"scale": "xOffset", "field": "group", "band": 0.25} }, "width": {"signal": "max(0.25, 0.5 * bandwidth('xOffset'))"}, "y": {"scale": "y", "field": "value_end"}, From 94c141c3b331476c9b1fde72e6ae655d1d31fcbf Mon Sep 17 00:00:00 2001 From: Kanit Wongsuphasawat Date: Thu, 16 Nov 2023 08:58:37 -0800 Subject: [PATCH 5/5] test: unit tests --- test/compile/mark/bar.test.ts | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/compile/mark/bar.test.ts b/test/compile/mark/bar.test.ts index 70939a6949..1aaf07e286 100644 --- a/test/compile/mark/bar.test.ts +++ b/test/compile/mark/bar.test.ts @@ -43,6 +43,45 @@ describe('Mark: Bar', () => { expect(props.height).toBeUndefined(); }); + it('draws vertical grouped bar, with relative band size from config correctly applied for x/width', () => { + const model = parseUnitModelWithScaleAndLayoutSize({ + data: {url: 'data/cars.json'}, + mark: 'bar', + encoding: { + x: {field: 'Origin', type: 'nominal'}, + xOffset: {field: 'SubOrigin', type: 'nominal'}, + y: {type: 'quantitative', field: 'Acceleration', aggregate: 'mean'} + }, + config: { + bar: {discreteBandSize: {band: 0.5}} + } + }); + const props = bar.encodeEntry(model); + expect(props.x).toEqual({scale: 'x', field: 'Origin', offset: {scale: 'xOffset', field: 'SubOrigin', band: 0.25}}); + expect(props.width).toEqual({signal: `max(0.25, 0.5 * bandwidth('xOffset'))`}); + expect(props.y).toEqual({scale: 'y', field: 'mean_Acceleration'}); + expect(props.y2).toEqual({scale: 'y', value: 0}); + expect(props.height).toBeUndefined(); + }); + + it('draws vertical grouped bar, with relative band size correctly applied for x/width', () => { + const model = parseUnitModelWithScaleAndLayoutSize({ + data: {url: 'data/cars.json'}, + mark: {type: 'bar', width: {band: 0.5}}, + encoding: { + x: {field: 'Origin', type: 'nominal'}, + xOffset: {field: 'SubOrigin', type: 'nominal'}, + y: {type: 'quantitative', field: 'Acceleration', aggregate: 'mean'} + } + }); + const props = bar.encodeEntry(model); + expect(props.x).toEqual({scale: 'x', field: 'Origin', offset: {scale: 'xOffset', field: 'SubOrigin', band: 0.25}}); + expect(props.width).toEqual({signal: `max(0.25, 0.5 * bandwidth('xOffset'))`}); + expect(props.y).toEqual({scale: 'y', field: 'mean_Acceleration'}); + expect(props.y2).toEqual({scale: 'y', value: 0}); + expect(props.height).toBeUndefined(); + }); + it('should draw horizontal bar, with y from zero to field value and bar with quantitative x, x2, and y', () => { const x: PositionFieldDef = {field: 'q_start', type: 'quantitative'}; const x2: SecondaryFieldDef = {field: 'q_end'};