From 9ee824c52bc08ea5ebdd76d8c4b241768a2082b7 Mon Sep 17 00:00:00 2001 From: Igor Churmeev Date: Mon, 9 Nov 2026 22:07:50 +0000 Subject: [PATCH] feat(pulse): add a second Y axis on the right WithRightAxis adds a right Y axis with its own range, ticks and formatter, and WithSeriesRightAxis plots a series against it. Right labels sit on the rows of their own ticks; the grid follows the left ticks. The legend marks right-axis series with an arrow. Auto range, thresholds and negative styles stay on the left axis. Closes #12 --- AGENTS.md | 2 +- README.md | 3 +- assets/rightaxis.png | Bin 0 -> 449683 bytes cmd/gen-shots/main.go | 50 ++++- docs/axes-and-grid.md | 39 +++- docs/bubbletea.md | 2 +- docs/development.md | 3 +- docs/getting-started.md | 4 + docs/thresholds.md | 1 + docs/zero-crossing-fill.md | 4 +- legend.go | 4 +- pulse.go | 17 +- renderer.go | 12 +- rightaxis.go | 174 ++++++++++++++++ rightaxis_test.go | 399 +++++++++++++++++++++++++++++++++++++ view.go | 55 +++-- 16 files changed, 735 insertions(+), 34 deletions(-) create mode 100644 assets/rightaxis.png create mode 100644 rightaxis.go create mode 100644 rightaxis_test.go diff --git a/AGENTS.md b/AGENTS.md index 28b8c9f..f3282ef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ Pulse is a Go library that draws live line charts in the terminal for Lip Gloss - [CONTRIBUTING.md](CONTRIBUTING.md): read before starting a change, a commit or a pull request. It has the branch names, the doc comment rule for exported names, the commit style with the `pulse` scope and `Closes #N`. - [docs/getting-started.md](docs/getting-started.md): read before changing `New`, `Push`, series, gaps, styles, legends or the runtime setters in `pulse.go`, `options.go` and `legend.go`. - [docs/rendering-modes.md](docs/rendering-modes.md): read before changing `renderer*.go`: line glyphs, line width, smoothing, braille and the fill options. -- [docs/axes-and-grid.md](docs/axes-and-grid.md): read before changing Y ticks, the grid, the time axis, the symmetric range, the log scale or label formatters in `view.go`, `xaxis.go`, `logscale.go` and `scale/`. +- [docs/axes-and-grid.md](docs/axes-and-grid.md): read before changing Y ticks, the grid, the time axis, the symmetric range, the log scale, the right axis or label formatters in `view.go`, `xaxis.go`, `logscale.go`, `rightaxis.go` and `scale/`. - [docs/zero-crossing-fill.md](docs/zero-crossing-fill.md): read before changing the zero baseline, inverted series, negative styles or the byte-rate formatters. - [docs/timeline-events.md](docs/timeline-events.md): read before changing `event.go`: event placement, guidelines, pruning and `EventsBox`. - [docs/thresholds.md](docs/thresholds.md): read before changing `threshold.go`: threshold lines, labels and colors, and how `view.go` and `renderer*.go` use them. diff --git a/README.md b/README.md index 8a56513..7fae5fe 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Pulse is a Go library that draws live line charts in the terminal. It is for dev - Area fill under the curves, stippled, tinted or solid, over a grid aligned to the Y ticks. - A Y range that fits the visible data, for metrics without a fixed scale. - Charts around zero: a zero baseline, mirrored series such as network RX and TX, a symmetric range and byte-rate labels. +- A second Y axis on the right, with its own range and labels, for two metrics on different scales. - Logarithmic Y scale for values that span orders of magnitude. - Thresholds: dashed limit lines with labels, and series colored where they cross a limit. - Events such as deploys and alerts, pinned to points on the timeline with a marker and a vertical line. @@ -55,7 +56,7 @@ go -C examples run ./livedemo - [Getting Started](docs/getting-started.md): install, the sliding window, history and resizing, missing points, several series, styling and themes, legends, runtime settings - [Rendering Modes](docs/rendering-modes.md): box-drawing curves, braille dots, area fill -- [Axes and Grid](docs/axes-and-grid.md): Y ticks, the grid, the time axis, automatic range, logarithmic scale, symmetric range, label formatters +- [Axes and Grid](docs/axes-and-grid.md): Y ticks, the grid, the time axis, automatic range, logarithmic scale, right axis, symmetric range, label formatters - [Zero-Crossing and RX/TX](docs/zero-crossing-fill.md): zero baseline, mirrored series, negative styles, byte-rate labels - [Thresholds](docs/thresholds.md): limit lines, labels, colors by threshold - [Timeline Events](docs/timeline-events.md): deploys and alerts on the chart, the events card diff --git a/assets/rightaxis.png b/assets/rightaxis.png new file mode 100644 index 0000000000000000000000000000000000000000..7aae5379335a24b7ed02fe8b00bf457e2683b1f6 GIT binary patch literal 449683 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

qrTmN@?uFKGQ*yMWaw#f7w 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%0A0Z@l7#<1fm@6{SnTmi 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

O2|M;&R@JT!PhW7`$kFMWM0v+P z%8x@5wKZ>f3eYfpB^C|uUe>kbkjGp&2!LMQtK8y+P>OZ_e z-yF>uqRNZa0BWM9L2S;AUo4Z}*jzIm=BJql)N_xDJ>oUFF@%+6WxZ0|-ukEbZkOeUSu@p(~NYS}V zYYmsjr|*pC)P93?WwteX7kaM~<=-aJ4l8PDHO^ju31+?hg9cud<^2v<}iaEMr?(Gwf^e-9wjWiunb0nicjAj zB|Hax&ks*mFrh?0xR=&T^I>O{^iBa*6 z%?kY*p}oqLJ7IP^Y#v!WpwwC*aibo`AbhJeoG1BSvp#DaIo{}=ulW$+10y>S(PhC* zxSXLY(=x?CZ47pZlr#U7+|LEj$-)HbLrBhDGrrcCw%dG^caZRfeYGe9ZUV@n!dth$FW} zmo{(pP5~K7iK;CPC2jqGJtQ^R`XGPs5FlD97^~1L9O^E3GUk5gHtv(!jON^yVpb$7 z)5Yk|z6C6^`Tx{WC3iQuO)N`;%s0z^27*fvh5O~^Pal4@d`A=VIsh5BL>u7SG z_-{s1sv!5fm4H6t6$Gl&Hgd=JCoioK$yBov+E%O98ST-$cdd7}_(_?xF88~BZZ@b4 ztI8@pP~N%L$Ed<0Oi=P&rX`>03+x#1q+=C^Z^P}#x-(T}%XQ^amk?~cP|v^q?URuz zA0Xz?mN-oh!A-*B^jiL&cZ|kd&Yt4RF?gO9?xOf%WpwsrJo|dRSkFY4u~D)H@Q!jY ztv#b}QSFEH)K*qoMBOxvMsf5UEq0mK6s%_r&VullU|^{N(6a?WPjh*UiVd&*)Q(K1 zAdk@?8z6>V+G^lPi)#NrrmxBP8n`ZvfUHwLDvj9Z`caZESU_LI$jn41as^$*p3k_N z8SM)(Z1|gXhCsBgWNjL?MYhS9aR;#!0aHRE=wFkNFsD zSpRAzmOhVhXC!yvHaR5oKXBaLibVCn`Ml{fuTL!uB?%1GFd7l|-zQ|>(VpM3;?<~T zNTT`RB*3x$*)kol0;g+auc0%MF<=3MuACa^E=Dm^ulyHDSN55ZM5 zNn`nK!sS~HD@2MR`4_Q44Td0j@@>JH7jxn>s`VFcRuX&Zo%H6pN+PScB9_`p@8oQS z>LY65Y`oP3j!Ip}OKV1ICMXYz*keh45YAWnM{i{cUmEhvw$rid*s=qO-zt}<;f3V= zF4BA*T*zq6Wzz|LYMO?Yp3N{{#nY0!3`t$s6rElMCa6?Ea4rMyGdAFMY7K(FvRbZ+2XZiC7ZTq|H zPN#6^TMPxDh&;qHTIDJB`XB|7nJ`@C#)fC?jJk0Bu?zxU}b)Io*(l?t^_8m3IvNxt&{u2bJ6C6yf)ITqf;ihdcxg58^NQW64kbEm&&= z+{)5BX@)_!rx=9TBTgbvA8EEE1C-Hz6ZD{^ce15his+1DYc!>If&);Zw?bz@?KYw~0d?^8>6HfJ ze~1(vN`@L}arLUQUn=b*Ox2j2hb(P^TR#(iF1_HH-}}WEUp@n@MLJ_zL*FZ=WQE2Q zUEBdL0bqbaA-VsPc-4WdCGW*&YyD~RqW-(|OxD@49M&aEjHRCw(w=4TkwEPTR@ zB;i~wzk<^RMl5t5azK%#Dr~ts;lIUsy|_flm0sP>uUp^Ecm$^1&v%-?8|{6VB3aMt zY?T;9^3}qV1+zO^=cG2zhaCi1)G78990pRtCRT}E85SZVKyN&E&!yW*!=m!vu1Z($ z`8;Z|CixKboAs{`ePgs?$vfP3nzxF5UeL1uMUU~$+KWZWn+$skTJ|ofIx|=(!BjY? zJ-=k_l1bNNi-edfX8~XL}# zRd9!xQ?xe|K-XJH!;>AqdF_tD=Uy<~N9Nu(vg+R5^Pf`=KNaRpJ#%*QVA#+7jZo6P zYli#G4clkj7p}Az79<&m4yxWf{mWO*@jy(Bb~-W`Er~m{@2zP1F@OR(Xi|Mi&JM#1 z#oW5h(7UA~NPQjtrqlGr&g{1j*oRlzewk_1?&IL6nM2D>4t^x`zrAdmI_Q zazokSm1$P0a)Peo-lW*`TjmdnalJpiK1FkAt})!DgUJP~8n^OwFsT@{Zd%^UC%)!# z{woun!XRYwb9o0Umps{V`>E+~+pZ&Xzgm8D5NwvMBjbFpt(=Dl!gzKII%=jsLIqfdrrqzfG(9eqyw7T$=LjVQx4hrVKHT>C3@A;^*@mNKZNWT^qS+r_(^G9bN z{7c@@HV8cU`25$%edZqDYb#IPvo0qB&cW`K8{TL+e)ri4U4}n?S8$;6LH|Km=wEQ> ztDqr3;?WmC_!jO)WMJ~1v@jSOy-;@cu{rZzc-Sxx$`%9ykPqRVK5Sg$J{;s@(`o|# zKb~Oj-Icx+Act_d7$u^7A1JnHO%Q6TV}k(&1plimtRH=Q-D0eq4GWCjD=)ckTIWfe zd{$MBvl2nI7dK8OR^3?J{)BAQ;sc`nCkJYyxgIbCN9-jLucIk=BTckxNeUddhVyQ@Chvy7dg8 z?sVM&4>mvLOJHzXL9+kwdirBvoh3J>B1+R<2ndYv{VDI2)K$1@N5C~bq}Pm#bMI0ZRr_%^ zMM2nQC;5iJzfPx+DE$pc4p9Q?#tRLKgY*iNu_6`Q=RC1cEkkA#63ZxwRo#Sr0H$F& z2uTo+^h}CSBCp@jh7AHzHlBnBLf*w}k2C_LXe{#Ns24zwPTr-qu1IM5>gmRBu*CHx z>>vvg7TL$nv2CPQ?eaJ4XHTm=i{W-P{=sMu^3>9vk!&ibv%x`<1T#tBgmjtD;rLW43iA17=OKe7bZOjHh)ee-o2RnxFQJ$H3ob6fLP4(49>F<% zxU|IAio19Wg9XjDW#5|uAlwyehBYNjj|u-RDMBMJ2KN&R>e$>=BR0`NXvm^y3jRTH z5F|`wlL9mfRh~+&Q4dXqC8p)fILD%DV8l=$c`$7^^14Vm(_W(Z4W2EKvm-BsXPBcd zOf+1ohPKa}Sd1$&4R9ebJ`1Cxk%@}-xmceHyRbxbOq)_bS@((-ga;6RO_)F-0`8-* zGUrIkCvZt&$Od=llK&1lirc(5t1KhqS|SAJ@OH&&*uo8B_RQN5FJhtK@lwEE%im&#eb@8d`9C#e6tj`2VwyRU|YFtn$zA(}Y_S;F2VMwanT%Q$cF-H6&8rT-%t5X2l~Os^uw zlJT*Kv4Xj$y~WJZ#ra)*GXAT$Cv7w;g)R?n1bNtfl?*TM60^0vC5 z!B?D+xN6girxi4fRM}4!gGn;~9RDEqVCl{AzP3@&SJ8BW4L7%nLak52%mmJ{a2Fuw zl<0`Co0X?9YkYCOJr_;b>3@6<7q+~TfFa!F ztv{Z#BsFuAavR-5LYV3=d6{sgf>{s}OA+mqg}ZRthlwaR%~fP1+t@mREBE!_ARhlA zzMa)Jic3AR*MC5ER=HrzLs>x67(c_QJs;%slMlrXlWd~_1*tUnnUWu%sUBvyh}m$5 z`geUkgMEJl-9dkVs?DrYIW>SBdXs7*sy+s+`faZawAlcTJO&DP7{X)AYYRH)rWsmz35gvdd`0yBjV#zs*oQkEYh>AXo4Rrz+*`E3ymSjkb8%706a z!v~-wKTiL|H`RPBh3O2<=gyCcap}2@ctm&MT5A-o{qIT; zI?|D`2$d062=yJ}q3IT_ZxZc#Mk4z&B6GE)1%W=iva!^I9sd*^rEzc5fC05r#&Lgw z8BWp4AP|Zu-!?L8%3{y_J(oFNOE%XzFDpE=!TDLZi^X5cMPZkWWc!&#VrfhMMU~n; zuf8Gj=#cS$v8Q43jT4~SqjZIez7?N#9**u#gSRK%v#rn<@eM$d3;ac^g6?!oV}l2b z4r+1ukoz!P63YC;WdlSDxm-+;P}7(w$&=o0mS?3m?>H8Xl&`z9HcgH25t)c=Kr9?5 zBMhQ#$2^LW?93zSw?9Xx^`7uwaTFEw&ewFEq`vRa%7wn?r!nwGJ(c{XY0?M?q zZ$_Hyh4bOOc4e982RBbcmILVg;9u-}a9E5--A{hPJ#cy#;1w=t`-b-C5v_2s5IV$S ze>8#N5%$^~jv&&G0g#O);Geao#kdHEFd*Jk90{-sL~g~!Ei4rAh7Fp7_D$-gdN8wx z)K&*KKmA*{j3kc+Y)o*_tl>}l7G{a_&^~l<7tR7G`3;IrZcFd`xE`Jz9ZBF-zivD?5!*Y|U+ql`PWW|5)A(8lCm;6h{7=>Lz~ z2EnEiO7tQJeQLkEGc>tPKe0q8X?Xp}Z6kN>|Nrd0dq7lm`af>#7TcQfZM%@E%=YnP zR%Vl#qA@V_Yg>0S<UC$`lo4+$>4?;ACcqTuw@gf-;&Z zDwmP#hzP?l%zfsZ-}9WAd7q=6aYboY{qRp0k(qOOzn}N>yq?$ddfAJ|>ZN%$L)U?< zj9WSpG1i!uOPuF`h;M@fW|@5Va%#ECd7GoX?UijKVrc^aph%wShtTt`@FvnD;Baf) z;W&@52h=N6*aoii&SehL?^XvY^94bF4bL6oaHL57EE{fChFndH`C>9c|BjV15vx=4}Wi zhI^s+hw=tK7MQ?6i30(8pM_8&z?$u@1e}hWkZPLrlX>uCft3rtLv)&4H9KqE_gc?a zZ%^oWnJFx?jSB1M)MvwO1BlK6=s!*_OWcT!6^-L4-Kdx|=jDRdb(8yLFLDkC`JDx( z(cvwef$#uo5s0J%)~;D_Z0wA;5B*keJ+Yqt4n77T!u!06+h-%0aCSOE_x%z!$QuBq z?|){lBxzk{YSWtlZNeXg(Pu(V$se#=*GN~4hHeN~jREvth}{GZ6K({+y0**G5r2vJY`wG0X*A32p!R2NR3H7hQ@S_mOR0RW#d4MJIUgV_P{3f1!?A`D96Njce26#FaB@MW4~i1V%0My` zh=cTLxVZ5PYB!ynVE#S#H<`lo>s3$8qW5&2c`Q9|40JJkeLnp6h0Qj?CxKR-@lLMe z4*v#Zy}9$lneqSoL|(?pP%AW|xZQx;0*r&*U>w|pG0BMGX}?m@V!A26*JcO>ptztGyuD0K7jl4k5e0Z*lX6E;ej~9V0n@) zJ%MEf`M)m@+kNiV0&LiDMH|+3(gQ+v{@s|M<}+s%UqY09kE`}Lt_Tk2F`H7ZSkqQx z=`1U{GbH@%+{D;4XSBTyRA@jJfXe5Iu>=gGn|h7NM3)gYwTVVH4a=@vlY%fPZ*pg3tntXqH)hE#p?~9R^o&AoRNH zhbKN;a;}V7Az_xohVm>Pb}&hg`7G2^Kbu{}B03tL6h4^|2|Rp?&kV;5f}1vlyxL1r zbmZ23&fd;b_VrNexNv}rg9eUXIhi$bH}QGNM?nd*htf^G#}|`CF_Jnc0E{S+;i4<2 zv;yxhd0)aAET}h<1xu~QvUy*WWfV$g9v4VvZgXhkd)Ss3478VY5Cx!zhx(!vL1oe}mr((vJ|+Pt1|(xj9)m+X+NH342K2 zr4!6asktzvly`9$Wwh;zB_tKiBs|pIY1MXBfh+^+@jk+{q=@9=1qI19&MmxrX{U?Y zeERT&)Wf9+Hcpx4M5Tu~V<2VTvA)py$V{`kv4?nL2#;D``Q8u(7R9tC3D$p4(6IqK`21No6-sp%E0l86kT-5f3ve7 zA>_uifyE)1B~-Uwxfa|qUTiQ6AZ#3}#sY-r;`1q0udXJ(nTj%3aux)9xN>o7;UJhT z^OQ~B6vTfpJ-X=kP(P!_0qDg=y32a1X*!VXaXuPdmq*~+{1J%3O2clKYdy;*(j7;p z)cF=4s$0O4Gg%2iphjTA#6>tefjxPiIo~36RIM4c7-SOMsQ?LS+pVhZMi1~dR3Mgy z<2sIIno165yYkWfQ+(%s9B?*qPy$d&n_9qR@pa&te|dO_bs7aS>Vmdn9ufCTK6ke& zD>T+Ui5an_c0HVb4G6`6W5JJt6r$%e@EayXjZ8TO0`MC9U#d(s;RYr18MGmyNkWw+ z0_$CSLbX${Zio;@m#!XVuel^X-ihNx9BxZ}f8~p5Eq2+yiZTvG^*hj|FHesO)80~q&U(*;I*>Wc@MKwG*oq=oNcVSehG6Xq~y~# zXM&D}uWc&O3A<9+6+Zuk|B`D%QqRY9ew!gX+;b6C_TH$ruj0;3lG|1##?sD^1k>%h z?lNCni+#v%k`zV>_?EmzI(KE+d;^AO*zsj7m7d`ESr-kB2%%6JV&n$fBeXU?6hLu# z7lbQn*_AJ^SNtunVLax{W4zPJeG%V-E-5r@lz|`u()o-}?XXI~j43nb-3Gjc$XYG4d_h%EyZ7n}#LlWm z&Wayvh~&o+1loYVz49TxO~5`zg@>15nw5|jsSkMHJkmXfUI&?z<^S-rAnH^;rxm>< z+ws1xWyK5@q|b1YJ-&T;Ml=71ID-cwFyWv3CeQ`sD|^1c5BI4&JJ6XWOG$Sf-k4Iq`-imo8!zLQDvfM#I6#^2ztD0S6%su`zbth%;(UPHeTtYAp%{L zU}i^PEqD#S(zn9$L5eLB8HxD6LZBK6?GbpLaeBp^lJWa1icc3#ZTmRD;nN~kbdd~- zpf&6RBK4=P;ZmPmW(SXj3UUC9k=m5)5D&fLVgSzmZx6A5?O!0)T9PH@@u3O2skSuQ z^$JX|fkDo})|lW*GOOyEgzFCEIwky@F@BMZbQw$+ITdp?y9bVqD^x4zISvSc$*y6N z5`64@1jwXVwe2vq7g_?dNCwF3WLylBT}uX-n{QvEXu?S|-fRN2YKdY4FTKp%i;SgEP$)i_*ydKVD@iF_!47vA-q9Fppte@bG;EOw$+1z|i3a z^ebm`tb{%+b2ht`1}X zy5!l7vo3+h=<&%M*-I%r4< z;6}AcH$jhq6~?u5p^2gY9!etgwt5Y`dbI7hZrlJ!qux7D^#Xq&6AqRk(OD)B3A4>j zIs$@y=p00v&QP=w?J}P{A^RfvPJko@8i9$=a;+2m5EvWKTYN0)t5Av9G0WE+i(2C$ zZNPW709AjDAg+^^@2#&83IPabb4l+QZcG8CwmEvxmwfw~2b<_Jb&9FF^Afbz7wJsb zAX8*iv__QJ=OxBo6bIiqE@&U|hXkgM(;~0wIM4i-R`flIoJ1Ml745*Gg{XT-CLi?r z&YC$u)Ade=wD=?fIt9A0&%Rs{1}y&}9qPI9&O3kr^*>M?fnPGI%{69UGI=Wor24wl zes6coEQPEg>J#4^0?D3BmDG2Tk+P^yJQA^ifAuKFK8p+ZB2 zhd3YYkwjGv0>&S)Hh{C<91fss?Y)n4g1E!oECe>6^crEn^N4? zckAt&Gg1deSw784+DNDTG$FicB$dHFDu(c~j|#vEj`>gjN*Ek)Ke`kinS zM$yRxdL+GiG3QesG{zlH*Vb=X#WZE3YZ}Yz zVe&=QwYNg^_;JL7)Upw>HS7zQShjHR)j&T1y1-;G6o<5=AJ7Vc(9sWk#2bOGYE;8uBfPo~ zp%n5YoWRT1LXiZCPi3w3uek5u&s6TTz6zflxQGNJ!(v!$KXW6Go^S03!VQqfbNLda zOqex+-;lm%j2|34=X)JKLV%dXUT=a{j+Jq$!I!YQW|;+=bbKY@Iqa^1zN$M%bRe>; z6H@jrwQIUBa;WP8PzCG_K^(?V;#fXUt3ms0)M>CZT^qi*Ot8n%WmeAii%6Pa z={98VXGi30OS)gmd>gkfz7QOK4uZo^H5`uAYQ*1ylxy>w2r`K8B1>X9cc=wK>XUbt zwF9pVuKi-W@$Y#j@~!Ly@=Cp}KMeg`aGb2Q-BRLilP~AFAGr*#Lc!-%=&!hmK9hOU zASvxXFEB%&D{r5SGWZ(zewpiV$y)v9XjeZSV^;46r((8K`a@uH2noarYK0`28Gl%a(aN^kJ{Yxr~BIiAAF*{`yz z>I@aJ%>}il??9HXq;&&;#EgWw)%L7mf4s6>n(_o~s-_RIBLL)TPCU6uk*;4%+|+?_ z-^?>d$S5>ukqi$^Jv8`tFC}d}^kGgI6A6oieeTH4s;HQ44ym;>NI5mTy6fz(62jdX zxPBNtvDAT%1Uj2wrgQkjQu&Fb|`FeI%wy}STvx<6vomR(|(*5LTYXM_2-X7TEs{xs=KkIFu zXk1~wFQd}Irg0(KG{WNZX>)kqP&S>ARGelyYUuo(EdQdnmreWqHT+~Mmzc(S4ZwUp zhd6(%ux(Cgq060=E$B=@@nCKHBXs8;S7jXgB&$ZnlE&E?s!`e11U*4n8L_eej3B20t9%@MZ?*5Y@K@y!)!xw*Y{T?A>AAqvQVchvp?uj>xvyP{y1I ze6}h|nOAU*t|?WVpPUq1v1ve!y<3hU5faYhQEaJ~NGP|N-0xvpKkOB3(MEv*x00 zlFa!1HT+#Bk2lfGH`v|}Ab?m{gmaFn9`rfE@|LYvyesLBmF4HS_6ES%ySu_gEd-;T z6=>j_80(Xf;?$AM@}qqVT>NPG1!8>P0`FVkH+2%zx4`=rc;5o=OHtOe1v|h5xi3Y* z_L(AEjN`yzN>1n-OBeG$Abf?onffo5@E1m~*aJ`|-7MOpD5L&~o(MR67; zEHrGON)$#eq+3~Dn*xBG!{Hs8|Fr(MW$348UXvx)Pcdzk)TdR^WnPYW4k)$%HP#nJbV-P z!4P_&wmpbBJ1*R+WZvTw!*y+v!*im}0k}!V=6s#%!6kbSr_?!%WtaS7HXXB#dMYDn zO9=EFeA1}=J+Lop>{Aq#oYImHo9Frf`JBdcA!wBVp1C8z{?s<*slYP@?$*GPK*}0P z?_)<)&1ByQHvJ{8oRDzQoOZL})5-qbQ0N{B^J7U?6h?c#n5ng~+ zoDpzQ&)nA~Kup(POM)Q%fr|v-KAIbGo>+Pd@k57KVi_o3mze~fc^3h-7qd}*u|b0K zXu4>+O>#K?g60sf2u`gMaAk>I8Y3qZ z&rI%2_M6BS6rTdqX`62W! z5JHbZ=R`Z(|HGhEu8hwF-?Xi&_KNCj@Kr-%x&O5=54O_x`x0 z=#Qi%sA=72gNmPaf-4KX_tFtL!QJxDp!sU%Kn3G4+<=#7rv#n%w^}`ye@^rQgfD@= ztV`zk{tYq7AmGoUB`G?ydFP9`9ZicWNU>BHQBp5(JFF=l6Lfknx_xz_o12e@%2{-H zw|J?68bP_ns4J*dS2MAa%B%iyzKWg=8}*G}Wh~4rs9eFy<~hM5i@Ii{J|wlAGl1ze z`nq!_ej`0YP)f9d=PV`J5kwK~qQPMH?jCCNfJqB4w?e$vrm^O_)~PCW9)!mlZfwsR zN0FV)jusiVLQ*ilrj*3Ekoq2^c@x+gy18K1uC@iPpCgMOVUK%AxI<^|S}L)B5eYo* zs~Ad*0!MF}Sru0quk0F{nP}CrlRY_G@*c74<9K~s6b-xh+xLd1mK%0}hqrHaSjI_A z>1b{Z?Q!-{=HJm#Qy@%8j0d?_&3@R!QBfpe!;I>ub3h?eIE*#D3aRGD0-4z!w;PTt zC=CB|#3UrmY_&3_PKQQ4u{w6G^dKi28bpDa)~dR$F-ft!7_y>$UU*9odfNe^-90U? z++G-5MlHLFMPF2B2YO^#5z-@7wh(oBr2DMSYvniq=a!0o9$C_#SI^e8-DXXpg10@Y$R~>@na;?~Yj1Gi7abKqEp|M8Y}J zU6qWEnZd@cIH2Z})94DeKs#DjCZT3N3e{NR?M|I@3Nxno7-Uy7;B^c>HXiU%4OPR9t+(L!`f-2#D@2?c+%BB!1i zrPXREZ9o@iXRyQD4(L&m+Rf)i7e}W0e-W8bT-qJauGZ_($}MTXj(wCJ#Mx@0MR1oZ zg#`_2v=UUF>o&;PMJ=1~VGr${Lz=+1vtZ)u^U&Rl}E9HJp&R)4|gvyBa;#>}a@) zE=b@7r~#|$gCCOfy>xc!bt$opJ^g4@mw3N7RQ*J3*=O?d|^w?e+iQ{jeEH7dhDaf1W3`YICG zS)AE+W=iMC?ZropHFvHa7UzDG-pd?4;0d?V8o$!+SlQ84{K3KbG=`LTy|eG)jJ1ow zSR2EZ%S8$~!;|24HLgHn=Zdaeu9w+MX;lHYnPW;CGmn9zsfsyQ zRkDtD9oGnE$F{P66TOUEJ71S{m2%E~_6fV@2hdEITn=uxMZf3%bPU5p z+6|WSIP=<~lex51tA*Q4pg?Tg6H$6z@!gB^o9i=@$}-Q~YX32gxm%^z7#cjgo;r2e zS46!Zsmj$h?Td1!CaJ*Eh<3t>!d;;iybI|EHTD&XDG*KU_FRSYEv74yUdP6AR)y$a zne;4Uix2O&iY#Asm%+Q+TwLTh7I)wT$hXc$A$?49!OM&XAmASB zMAvJrfz&T_6I#B04~9l3JYJgp5Tqbqc?}LWod;SJO=3be7 z#iW!2kxVc+7(Gs=^XS$8?#r0)1(Sz&O%4+jSa z55Rv^SiqQja%JxHhMm-NEM<%qSl>FH}Oe(dWYyq?TbM2pM)2tc>+%6eR0X2)Ha7p z0I+!B9GgQtw1x*n&y(Ie*697@5YA=V(R)_q_M`+p_XAv)RJQ2DNsbkILV>Ig`pgp3 z^(~kYcQ2*ZV5%E*JRp|31GRCDXvSGe+wW0X<2qhv-)Q07dN=g&F@AVeXik`ERX^pfs7yfY+~Mb1!zo zxQt&9w^CMvY3-g6IM`%Lo} zgI|UyXP{Pb`3N2o!oo*`5&MDmD8Ham-GX+LNbv!wK z?|typ`(WreA-pw@T85{gu_E|_+IAHeMcW(=Cn7NM`X*H^mJ05;W$T<(be#hB5sVD`+2h+j9w-&pw^rkMD!a*|_wJeGksIfMVO= zE_9gRraU_mEL}Kr`K|23aL%f`L3vprYkkbK6Z{Gq6+c|Z9+s4X>d+=T-IBnz#HyH# z*v16L@whkSWcUM65YU=sRy2RA>9lY)D30s6Vro#h08=Y&8RGdmfGa6DH+alj*<+me z5w+Z3EbOJKQ!xkHhTMg*Tm*+I5o*BsU|Z@b5N|s(h+=cs(`^g-ab>V@qo_m=By-$o zkLmcYIyVMU&vBNkoL@KOZ}+cO;zzlt1<;ro^zKwR4f0b@{rU`Ko42#Gw#j$yG3SgM z|F;!j4KT?SQ*?eTz#mo`cKryEj7sxmQz&J?6w10<=Da=X z>5g#f*FV4q^-DZ8F}vk?;{|2|Oo-n|lbNz#Sv(|;fy`Sv!E+J)T+J&hFZfP+e^zn% zsF~DTO9OU0U3YmbkFn_eN7jm4I?)9=K`nocn$Y!Sb%M9mBrg-qRJp~CCu$~UKK<6i zZp9Gu|K5#uxi#1+xn;g}D!Yc2;v;nIyVJK*`{5(J11orUvqm+R6^~cFmlp^C(@3|1 zIL2NC3!9%2w6KVx8@dg^AySpn+LolRO1-~IefrF;1#Pao&{6Q94<29_iLNBL1?i!f z;_iVdtpW7!q=yX5;aZmqT#ivJ-8E56KhYZ*qc3aU1!(~LCx74ATdfyi0PFEXGZoo$ zW&glB{nePGMwhEnp*uW6Ws-2717>-xzx6a=Vd<91CDvn6xH0tlg9$Sm?xtHKcD1=4 zWRHqjLVC6{d+M?$3B)-0PyW7={X@^}Oh6BJh?rcaV3n~!;<*m!dRa`@Fw(94NrAokujTRHot2(FNz^I?Er+tf{Bl z7P_|#%&wlL&q&OQaNg_57)JQpHpwd9u}Vd}^6c7f5o2V}!gp&g+dsnS^r^qM(n}Sk z_l0PcIqjJo;!t%#K9ajFSygiWxu~^V zjTlSWzl`S!diK;!yz`zGcrS8iVS&yRrl+rB{oa#@EsxvVtGv?b+u1~ZDi_ac@qbwWD=OF^I6y3w(m(P zf4ARrGvC7w2m_E2x})qv>@@P&1P0MJNj8w|Rf@u*0YSMl+O62vc0>{0Iz-a7ibf4YJhnK6Ue596V%b~onM{4@?~8h=W)sw4xs25BVYE@q zRthLVD#w7jf3!u+1ItC<5#_lv|I6pdu(m!9C_iFH_n?NptLn{t~<3vpb_4x0Q4+ReY$o{8M3fy7sNC z>{2vmgbQcz#(STadQVEe)sY0-OB|cF|UfgKy+7rcyk7~0h9XzQC}bmp~2ntldbL!xV$$y=}#o>G8*@f;9SMR#(mHi9x$et<)iSpt)BeW((BweXgka0rs)ho7{0J|D{lhhj>I?7=Hr?Nq1fO-wT?0Co)-Bgt zK3CXYRq6AowYf}NDnWAcg}JlewdWw^daS~Y)4}fjQ^6JCsG@Vyd22@flJC>XOvlxh zhd5jk-ek4E8~46Z_r%?;d!%Kh!S3m@e4o)F*BFKx=bsr#&;8eepkjm3JZh! z&2G!-n8VZ9=3wSE&;*(hJnKwl4m+BAl0!5WhvbiWV}joRU&TEds=sI9diuyvoBF$w z{>5w>b&Kjqp|K{rYk=gC!DW%cx-2F|rQY&_#5L82wEA6pg8i3Eoj2J2sZ0DVJMB_I zcV82>(9ezgq5%6Re^IR$wCM%iA4cl$6(n!ujV-3C>X+I#=$N@!wG(8kMtF+*Uwi!i zOEqV>hswJLM}UjNL~4(8yH5T=!0F&%Z6tL%%X>DkZ~MD(?;ABsTKz>e>Wj4SmVqCB zE&HFAACCpIx$xH52hX04(Tt6BXDV&!#gAy`B`sPKs%FaYg~D9m={D;Aot23j!ORfY z<^w$o*BjH==lxwtV7G{&2B3$6pgJdPIMA(-F zwj`*Wnc$gF#|KrV0P69-_5}OyxZIN9Iua*a23C*mTj?KF?ES%SC=+E0_nUEhJ&}an z;qS&h0RV_3U;2w`?1#aQH6j1~Mb|TH?~YKqG%#1jrfrPT6rX^@qoZDG*(}TNOKPst zcElIXaP{;hHGN6Vl{p3VB{h9XO^*$C1P4@KQqz~z^d&WYNzK(-O)cIVd%cTA1|=*o{Lb(v)MN z9E}?4=C}Qk@Axv@Ul+Q!)WR-E1O4+R1kzwql_@kH2Llk?>YcVHao6lm3^l!@3pWtm zSxhSp@CpIDSBR(kd-I~SX`nihq5UNtNv9>I0lMx=50C-~pg&FsK>+IquPP=VSvml@RQ`ak!~u*k7P**?ZACSnbRQRiB&W0TBtZ-pd=U&TnT3H08WH zV>eDh^v3rm>ucrKIH)?zF^aZJD&Nw+{J{MJ=JJ6FAu+UUv9K%ciwL1w^X~6sLYt zyT?OKCSaCcLru0!aMz>o($#85(#NB_Uehoo7ic8HN;3A!O4?jSY2unudK1(>(-S&}f$XMtA9=V=Hf6W|;3MJNFVOuA4FD zTa)?l(4GIoQ20(ncjM$CRrOEFmYJK z?#AEsgHg;KoYo9=7*4iPaO-cYcYO%*gq(ukJAp|__pHjzK0IN8_82>a6Xk=%mDv}% zs~yDpKtCuqYM0nUVG>a37a^xp;S25taUT$1yYF2rzFi50|CB8Y(b;Z;#a58o#zY$9 z%qZH%iAQ@O7wct})UY*jo#?21fw!q)qjcpcb~ueqj@xcD&s1cWPh*v!nDq}%C2k*z z2vthD=u@1`5~`MR3vDY!b6FN@MQ0lWfxW90O|sY>VJMO{vB*^&kH!-0me_|Q>JNvM z0C6liR=1m-=874Z8^)>Ljc}jJ@U_yJU*(^>}Ov-@00SI*N0wvBp%R+5VRIRJGmaNRpJ|>n~ zuw#nxoW&=eqd4CwuN~O@NRfR%^5wPdGpzJS)N<6CR^FmcxRCUA3A+q>Yej_{GsQrXY{%_%F%8#OcChDnw3ziR%FvriYzmpTJ5{Frfd43>z=xl#=+1y)?4zNihTo2`20AvGPLUR9K#M;RG8jdO5j7hUWSmV z_wl@eL&I_~>m)ori1!nVYO4ExlfVpmvGhT9=x){0_Njrlo!MKP(MVSeuI-GRnSXL_ z0kwLQdxt9_>Bq@s{Sv{2!)93cR&?dT4TCCXH>_~pX*YDCxY>Mt!E-PC1H@}T4m!Rv zmi{qKb=PTDr1Ie&4&rChe;a zz!lD~ZTPRaMWu@V^HG*w;}v8x)_@k=kG1*?Th*@CLl33D(!95Bok=+oq~aPj&*_Jn zyRL)@JICmoDnD+1VnJDI8a#!DEfKe#c_bqB$%IJ`eq{$oHln>}Ih`^sE(b zOIKb)X_BLkpTmBq z^Z@9OPo&Qte?@Awv7**QtO_MCfItyuJ%# z3MlS_-Exw#Dp3*DI!eCMqp(e*xf&ZSmLIsS>!99h%V3`WeoEbsrZJ!ojP3;4Mk`&; zJ}LVym_4u~e%iq09Ramr5Njr3f&-4$#{ASdX( zl^rkC8V33OCXkk=S(KI+l@4Z<#b|)QiP75&kOZQktO4Ifbr`AR6=7X>=xp0H;9_>M zUp08xiJL9(Tm5KqAzfqUVk)T^|LPTgEzr$ogmI-?^YrUp#o zVK7QTOSTbliH~WuR;pzZJ`q+_;et)&RO3-SS0>lm(iF{Aw6njR?hqLHIkoa7aZY=g zH|qr;(%?@u{(8esfP~}76mV%Q8Go#6IYuo5sb2QZjFe^h`muG!ilo>JN_bYrg*wNB z@)zLymc8 zMC%KBW?Czj4PJ+MFXltfo7`$O&oeQ=(wAKcV zv83oHC<=hX2O`Wl^ts7)R#fbjoGd?%wU6kWCubAM{$x{c zkm3QG_tJHObr2NsbXl6J*>6C}MOhd#4>7xVLNT5v{dj|6$y2iaA8DO=zsoBpIzxC4o7n+IvS-g`t@d5i!jM;8t4~i9+es}oIbXE|V zmyJY~m3@@hF!?_$1P6dvap`x53lhc~9I>M-iO_}cpe-OKur&fdOn(Q!LNQtmRV+wm z1UvlZDIA!0h<=JdcbhIJ9Tb5NFqHXz4gm24&(99Od5TqyiOBNs($(xV9-7Rj9XmBG zQp%i*11aJMfM~gTK{_bd;e9Dy3r}Lcu~NKVQO-hKU1Mrf*jvDeO?Cua8``MzT9cxA zV3dBW-Ieh4KCq@LlQhQDErqz`qE$aDO|NGZ04&!nnV{9UPZ+xVow4DjO?6oz%0t7m zt8>@`NvigIRVEuz*K7{1oJf~W>a4j)3mfuuHr1a3l@Id#&+D*x>Inr~9uf`yj7tDY zk7DOg*)X=$`LyPso<8;n6j3tfezkL~4vPu#$KdB^T`Muy6`h*%xUnYOJ+aahLz@es zmUvuaogeNP8ugyu;>&|LSt<^4FvR=OGxL>V|9MqEFErwaESXR}cxd2v&;@fyoF-Rf z($cD=$>HpVDaIOAy|K(zqp`OxZWyXn5}T;>6+P<2}#9i zrlW?=-^ubx`a=i}l-i$;l=rV(2O5!hB?^5N-F5g>-Py#A(5b0nPpb8F`QW0;m@d0v z(MNjAfDAb_!44-~rXYU9!B0DPv^pw7N6zM6J|PIa6i%ibU7w zaBECS7S0QaJ_bMM=NS39;-npAGaCw09U1HORjRfGI4@>JfrU*sXJI2%FIo|3FV_*P zP3W%r6Ik;O#WTbFDW3h?q~5SISY z%rF)xOtpwAi9ZHWE>g1E{*vKvR#?mLwC%^JCK+v+L07ON3|+~A`c1m};fcJ16zTS{ z;r`_McvQ*XL;ZIN2jZ2`l^Uu!YX`U@J_9zJoZ}I-DkO13Rg+2AdJl|z22&qv-Rlgf z>MH&i#LzR4ggBIu99MUKbY^0#ni;0I@6n2r6TUJ_bMFrFc$K zSX5VPKbw$vJ5;(C8r(q2Lu>s>iF(n?>p;}BFE!g>eCF79ErBXG@)ybBz16=&ZeMEF zW5MOV)NFdR?8sAjcA#J3pa8$l{3~7drDi=AgYQetdalCTmzwoh)44A-duM%-5~NCHeTyETI2SetVw!mRCzo&fZzm|J8LT&PVN9y0qA7W1IeOtw}ch^kIN! z9{T=!ZNDcJ=f9cWamy2vV-`zm_Ux%kbx{l8K_RZ%Z)esmPb`n=2S2FnfUT;2wPgJM zk;NyRaQ?JJP#@P0$d5nH&I$={TJ_4?>zkJFmO_`f4L!a-^~s8f>r7qFeE6g_$Gd*V&ce=J*EbXCae?2#m`xdNYM*JQ zs+R{Q&Ao5%h@_3Y`^F{490Q`&hn6|BLF3OIxute}kaBH4PPf{tz&k1;)fE@7-N61p z@4NVVh81^@EE!e?!()PDmmtaTVTwg!91I`A*CtMD6Xl*=pt34)tfI9FS?go4^mMHP z$wOlJ%N`UBfZlxkDyObo(P%eR0X|Eoi=N>?(3XYMi_gf0=aEN??hBA!Ss=|h>Yj+Fh1uFuQZaqM)d`y<1F z?JGl!lJ*3ZeWX33`zMWU#gNdXy8{=s?j7nftC@!dZ|!m(#Gbrclp`TZWVk?LnXv!y zYI6j_Iyi=6f_W6^P5c5JCZqbR#qq)B-^CqhjR~%#n+8SLhsCCrON^4aG5QCh&d;s; z%>G&$l@Y2oX3Cn4^kMqM#S4AsTOs;=Q7xm!tGZri!Z3EqUB|Vfmycv}-)^5-T2Xwi z&{V@TC6p|MjTq_HEpB`+YVE%Le-3_w_er|gLaBZ4sS)w@B_m*%jXedQ-yCJG#WgS+ z<~U(^pBWyJx;HMq4B`R3|KjWUKezGl!NZ%#OnlJHWZ>*~(h$JxslnTFpM1q53eS<@8`lMSot1-mOdM=_`#o zcdu><9)|@02jIRQe=@Y9V_9{9E@i>Tc^k&aTt_u1#9}gqP>wl}wrmI@0bh1xOm^2q zm$KFYcPjtM_xfc=z8a9wSw%TEk*+ZATlRUeRu%$&9yEsM($#`aNxuC%1sI2k#>Z<( z5PhRPooSx45y>*e#0v#CAImec|*7JQ-a)6RQe zu9#HxdpJn&dP2g_&P|Lh42spqG2LZ}FY%|WyHZLGXO9(K>DPGx?A=Ih>;~<0iTUoxKxbW7;S2rRLK zd4-N`)-J4Fp3(elAa!r>SrW|+@9?<>6rqWGb?)EhHV=>bnO*)x=-hfh-+A)(r412u`j^We?enbq0cIWV207CRzo3?IQ#p>+v@izT1J81 z7Uys`f;J6(vGF7J#j0A{YqE30>;A!8Y_js29-i8pGn4lva8e>0VQ5;;mKvfTh%9n%ozfNUhOiNjYl;`RA?5-6T%xciRF#dfOiAru4#!-cPtA? z-VX`&3^hj0;-5#B^v96cKQB}1vP)EQD9xWYHb|W>uixD=8~8}JQiJ|gLwM9ZBsF@* zQK_Jyja(f_45;E(ag-z!07Ck>nn&3}2)(4`B>O`@31-m*U>pk&)KKmHO zX38txO8PJ$8RL3D7=!@6aH7lJf#NtL`NNQgC3k`{ke=#Hb;X3aMr&YiCJ*8P%1d?> z1q-Axqq)GnCoW|eL=43yIC|R6;h*a*p|oq2D;iloT=geAl3Y+GVJi_Tk@WQNZ{CCF zRyW+x60FgTP@~<)P6yJ}+@zD66j=e&M_Fwvb@s)VT;DbC_>lFdn{rCd3`~km*dTAf zId+?-Q*`pThwwC=n3q|yMCv{h30$=fUCrSUDaSbLaxMpT8!m?eaKY$P5n!DehET^| z%)n|4WH-9VA9`}+8qAP&jaU0jsa2R)d=+IJn#di$%!W9BwjqWZUj@Sncq>D z)A^qGn%JkX-Y9Jd#k{0(LKgd0j^Y^*NQ!!oGUn_o z`jP&ZS07;_w{umLIN_#z&C5 z`x1J>um4vOqBTZRiABM;)1w-eajCxOkxu61upWLz_}(OV1Yrt1!2_uzm2jcV(J)g4PTba#P<-{V{aq%{WEFKXuC;nyEJvZ z*3%&w<^tsITjPE_@kL{SyE86)mBw+DZk8YG4WNLIg+UFay#1YmdX!KJV$O~WwIFtBdNsNBM_KzL`qr3k!!2 zL!iDCB#{xWLPdRcD7%ExAnaNYnVJ9x7T-kG#`h2}8Z1~_K9uLui+z*7`p0MOT1qp= zB+%C8d##N0av2sgFWgidwStrCQ-vF|*!OkzwnCe>S9;VM=TCvExlwIjY4rA_45@7t z3b~TZo{b`pxrX3(6~z~5CqQRdsytVw@IyU8TP>8 zz^4(HUiUS^JJm2p=IRbAx@$)I3cViW4{uKa-L!1>j$Q$Iwho>$uWPu(^@6OcWsXcv zcTUVV7U)cz$0zqX*}}_}o%mNoB^KGY1TW@ez;^S)n=@?chUTdDu#>05OZ$hJZ>J9r z&1Z&7KjKeg?hL2P$)V1{rk<3rflmp2jZKrJ6*E09G@m6IrNMkQGjYzgV&isMJ}#z5 zAiu+qVu#Xv0pd*i1L(&9BHsAsbW;G$44i3JH};Gm!Q@n*F8Cl*c|1W6LQ%jJKv+ru zJs{GjSO}e%A?MWqV{+iq+t*gzFV>iU3hDd`-c!-Wf^>d zF~qheA@R>Jw34kJpA419H-N;)3=%jYi61yhMq14B|Z|2O?-yQBmXXCjS@g* zUr+ayYm4`(;VnZIpCzbj(~Ns%t(HkHIMPV} zd=$i=~!cy3PmB@%YU0iS&X8_$~3cGhqcAR=>-UF`$&uXG8PE?NV_gKc_H;?^5rTs;D z-{7)a#=|MbKAyh3@$=JT{x@*Y36Lf}b-LiTcacA1_(?miH9G6NH~*fr=u`G_*na)K zk)8f(a({N{3+=DGJtV;M6_1TuG4(C&H{pN(o1{yJ{nUOKJLyUC>Mzyve%kzL^U>#- zLg?ed9A zccG|st4~z=XAqT6Zm{e~bH&W0-dYNWb7u|YUBfMz!`KHhidygQm#{vp>5VX>ZuaQh znv~b?es@Ief)Clp;T!)xI(NNpM!YI+AC)<=ZqXgypX4lVS$}%%_L&hiD$6I-^0tV1 z^Gml3%&tZ`Zf0SVqVdy^q5aRS*pW6QZv+$i*z2pZv~T~a^UKi3jesN_ruXYpwTdni z)qMw39L%h#njPy^)=D4yYDU1kQ`=L=GCPOct zgBNZnvD6oxtSB0;ZI>|Tq9*_zFKAWLRZrz5ool4BkWqC^<9M7IMyeW5S4UVf;jp#RQ|mk+QeCmf1+cjfRWa(e0IJ=(vn~U38(*R6 ziXmS^eNLY5^CB74E(O#@*k7Ab9i6xlvMm=Lpn#UE1o=BDtbW5^7IP+~7!&y`DW%?6cfz$#k|Kwt!;Ix(PK@&o=|`szpR5TlrJH<<(Ivj-F5hUu89jv5(6(J_;GqM){85K+?>DNK+?INhQ)-x53Oo!u*%UD@l`Cv`T@4 z6mr7_?aQV6g@5%VRDS?(H9QNAA80^g#)4e24MDsM3twfZ$d++&TMKhjvAsoU1ZWi7 z87Z$QP^$M*@7o*h*K|)el=1!Rt?b$!LeJDX0)Ocj`?}k%$Sg>Pp^3iw?O1hv0T~!z zHj&uD=i~H#UrkQvK*DEirmKAGkQswD!zpl(z%pfld&@x2-f?J5g;acqoCSa(lu8X^ z`BF$&Lz0cg7IQQXYPkrIC=MY8o>1!IPz-~zX^P?bOG&q z*d~*=1t9E5lhQ)hwtKMRP@i1ZVae4JFi{Nx|n1!LZ%KBF|GR*qIS9WG01np}or*fRzX z#T*lWripbaAA9#$JBkYRvw=!Ne=eszn6GHqW=exLg71sT=L%mWV|8#GXFgRZ;9L5+ zVZK(NpdP1BlTL2|R%{x!yKK$=d)c7^{sfr@qD9`H@fp7Bn(SkCh-M!K^ItT!J2ChK zF4B)-KFX0X`L>KjwXo4QMKvr>oPDQ7`{eSGm?{8=`D)2`Q-_a&}0Uz+?M{a~q9)FF^BN-b1 z*f01zQJ+9+CkYe+0#4*^LTkw9IZdvF-K2k>&jEJ|q9?>7G>NgWYT;ULf46o(!Z*;- zDG`;&a8()a#P@nP7niL1}z1PG_zBqqlXNX|I7vv5-Xd7t1N5&qI zgrD(F7L%2KkiTfaW2$Qwv?;o-eD%Maqe&evwLBSjFy6UTXZs@z=}^ls-+mPfGc{ZJ4SeNV_JsrOZV*WXbb4S4Pc z$S~e`Vvx4o$64=`ALYSrl4V7$A*nN8nbTrex$XnxMhWd=8p|L4PDmtGbReb&~ zl7IP#iYECfk}NOr)t^`KZoU$~gHQQlE*6>VCunI zV-EzzVqORXZdeLey%j4Gb~+hdAs2K#?-eB5h(v)wQEkiRz)tfGMbIb6^Yig^2JG` zOKm%$f~Z>oqTCM|vZ> zY&Z>=lYTKlE41A_v{+0AK6X z;2@$6S_M=(zbARD`bGX_7FsK*GcbfUvglzEU}63kefS4tfWbGuY2O{qkc^aV1#l2UDxUxjwo;>*7z>MX@>z*EMu#GMb-< z0|>@9>MDwpLoO&E8Ob;SZakd)AMJ4rhp^`GL2Knq5+!v~2G}+f~9-C0-$o${c zN`WUwMTuw{}GnT&n0{QvG_?f+qrtHNgM<1=mDE?6Yaf=!5jYzq*a}v|)?Otzhn8 z?<2hc_BXup!ah(beg#QZE!<2nZbkODWY-HBR}RlM2mh;T9)cof{J`WhVyLJUDnDnaM2mFDgn0442hs1rqrS|&c;-+r)y2lhei$-ETyigVGG@%X zM7e}rh28ji=rSSGol1x14Ng>hWDa`V@Yf=9FOY&4t!_mg4#xxF97TJPh+!wZF2@(r~byM2kz`W_84rWR!%Jc>vN zs$e+qK#ozLU@L;$>lp5=fuk}WG@Jkx!!rY$Qh2+l_d#hyMt!MF4Bog$c2+tCz+_38$y}xHe=Cf`E^5*}veXRc;Pw|nwngnT%3Uk|k3km}_Q3nVsJd=WU zco6Iho+}J^`lP%91jG2|I1m4=^mJe0b)ZBhmC>^%`8BIM3IhNlgIC!wf;ks;E6-Yk zu;W#e$laFwY!3^ft?Y#BAvcB^irOraox&Z>zH#qpHQ+cqtDOInT}?oS-RG8F(b2Pu zk|yXZ+(Zl;UsN7L{ob9D9`L}+0j{HQ)JOq9i9%cEkm6rwq!>fa6!2n00(8JE!KNI? z^5%+OF8vmUIcC@_>e!^t13^^(>}nrn>jEwt`{_2ib$DV^I(>__o#$_DWgjCM4}mox zXezqSOe}tUXC=by3)=3cRswHL{_9!F_MPCQu`rO@9@1REOIzZ;id4D-H(iyuHn)+) znEw~l<^FALyqgkQ;-UsjKp7%EQ(rm`pG-IXGb8D_|9Uj=^9jc9%GgI?Pg1|xZ8;qS zUQx}#%xiJw@xA5j#UVix|8;8_-b8aEO#i3r7mEtANm{Q5%M~~##HaKwT#)`8b$rX( z|NDggnqy7Ke}B>S%-Xvnlr9Zw%%p9M(G;HmBzlpC8YcCFv5^K(0(9iPk+~!RHt`EPybLUe~q!Qq4>Y z`b0=Qcu?@2I~#{DS#vX|@~W`Gsls*`$5@7g6uBZiX@j%NkJn37GoG_8hEZ#));DPS z!ys|{mI=z33$S%(mjCDCJmz@E1H}Mdt!=;q+9if_^0A5EJsw)XwjK zu^P%I4f~y$8wHAEpvx~rt5DDuTv0UO=us0k%wfb=pMYsVj>&fCu@Z(W z55F4XJ2MXpQ-Q-NyKTgU+K^K10eSVX-oj^t( z@}7~3S%$*o3`>$v_L*7#9ZLdtfB7>31= z1o32qw{eLfNl5n#v28IV0lpSPl5c3;5M7l&Z5T-y7DE!8z7gKqh-gL3k^+}b@p9S0 z!?2hoLAg!Ll7>3aaX>9!%#z5}e`1#8yZTJblEf^@KmTF`VOR`FVn`A&V^|DHe;+Ii zi^Lri&&67j@6s`mxch3@HA68Zi6Kc0Nzf_bV@O9(KNCZe7?RiqNiigeAxVw5I~*eA zLQ&oTt!SdW!FL@zkG&pJqlzeRAQvW+Y!btaWkYa4^{`UU4ws&*aJnkPS0+#?Z6VyTN42x+g>gazo z;(u-%udLogW&hJ#*{q_$Td{zJr_sV&c| zVYVyG`2ESst|G(M0=h27Q`Pjg>GSCQUu=(K7USYUb7WZ?q+>g$ZqYy8;+QVC7DMi{&c=_$C35BV(54n-O4<4tjLg|DB(}`;;BfJT|YrtTd z^xWWqvcDOuOOLrRruW{I-#py5_P#0keNF}3dvw_#d~QZQ3N!-K$R0hhRqnK>ob#h( zvO~0N_)p?LT~JgWtY47DK*aWum@B;G;-)I!L#rC zufh8T{gnR4jCg4NIZe}p9pAv_4Bo}zx8}iHTg6BtMjAhFGx+fpBhAv>yBiNEm%2*$S zM-)NnqW?y?AaFf-QL6Vn<&k;iztnf(Sic+T?%V+|->{JRp9tIkgE1g5RI$eTvd;Z% zQ0gTADk!gAj#=v#-_Ea(=>4ljHj9C&pQ{EOt(TUBH#M$*ut(Xjx z{(G>ZzF18Vs|oDwSv;OYtR_s)7bN!{S~a2T;5SoJMHw*M z&U0?i;X{MDDwct}@^CqJ{4ICwDzR;`SrT#+v00Mlihx%kV+e4=gF_FoSyF74WUsvr zF5`u|V)1H)VKF2D2o{?qU6Yq)9+qCU@hVpg!=elrf{NHIDF{7Zg)l6Rlo5$L?na>? zj(iC74hPE^xT0U+>XLz%Vv59_NZbuJNqdkQ!<93J0DCuhgQ!T{iNu{q++Eo{v+BRW zq|aiOBxXrhe+Z?RCGE3appJwX%YFB@%=^N-Rrlf16|*EUOCnHbB$5uXSyF746q_Zl zoH2+Y>3>m3x^S>?Te*9=UqaYeP%#ni8|;}ArgFe#*Uqs)<6xLacI{T)BBMV|ffg;> z1giG&2&N(E6V%^>XXBrT+4xHW7Do`a{Hu?WBdT+BmljwXyECd4J#W`A4-6<|d_6%$7NwCnJjGb_WOr#oj2NR8j1W>L2Q59h|mP zY$y~P3WwX;$WaPnL!sE_A$H;NdI`ifkE`0|A-1jyZlY&%nBg~!i>>P;y1H2G!X1$J z40c!Mf6Tmx7_F=27Hw~WxhB;Ox29!{@9S=bpwO>(Tybhbm$|97JdM8I$MtHRx0>sp zw*6f4S#;SK8oDVhHPBU$@((8bpV_CzGE}+a6Q6mnB|$wrQiOFHC3M@cY)+ zaPxK5|4A24<)3&@(XyeWuezpZ4z!BS#34aYY*}Pin1o&DM$4nJdVYa70xzDD@^rH4 z))%%f*PUFUKH2kAZ1yg3=N~@Ml<7%Qjn<}3tEQ{BNMZtY&gsV3O6(n&Q_vO>=5{QH zYvs3;4&fab~u#o8s)u@l}x(w@B7F3+_=| zvg|xlzAOr*AA42JtLeR?_M$(hegzOBnVHm42y^^}NYnd?)=8(&F6?k`lLTQqfNUdx^|AFuCQx* z^s()F=hXZjr?CZNo_3Y68_^g|Sz|$Y*n);6r(?h4D0S|~lw+!A?&v*D(vMcBVwg}T zBzaUZRv1pE>h^@u1xLbb(%(0%k>@vPJV(<$Nk|;tawwwv!P1gbGVaaSK%l+t()P9| znb`4tM^|{xKMKDw6P(d=Ig|1=Q|PnWW%}*R7u4TJul>yD*^ez1{LO8bG;L3sV?TJl z{#4(D&sXnm2DATgRkh6qoV)k7OJ{4SSF+{ptM*wRlvR$M)U9dLGw0mc5I}bdyjYY8 z;}nbVbEH{qd8eZ1rD)H++R6=*H52kRa@ThAlSO5RsJBP)5=M9|@y!oPsGIT|)|B9E zg80+uRpY>$ps8Tp!Ss1Tw{?on*5!#u8>HHECPj3thlA9ZYLb(=e1=l%?b;(xw<5G; z=fQo;6aq#rm)G6D z+nKvCtu@E`VQ%{5{DvEu?)ZHsa6R%97m}q%bIOk_=xL#_)-%_PxEqt(bG`Q&I90LQBef_uA#X)to%COL#QpI_AYGs!Gdx+ zdw%EZVf}&hS<%TETMFGi&`9spZ{tkP!gF+xtWWOi~6@n5TPrd~FzX;p*$5MOe$ggVl> ztaV-Dt6^&{-k6mawbVP0o*sZ!={u65{nxose+RAtBIBWqJ(cjkFYc*KjcP=fIP0{} zPl3OqRtvVmuae8%Zx37*uy_jUCnCcq@@|h>MZ=X+CNe8dUi~E(MPMQ zbthjasgZk5>Q{V{1{{-}sTCYb5>h;SqKl2LHtl)-Lj+DRcxl#aF515=jPmXpFvIdY zyK>&CwvdNE_Wrmui-GEYkAId}4Hx}h`I%1YmZGESJJ>WpXJEmLv>D^GZI`_I{@rMX zGSLi`AH`>c@y+l8aw1iU?bDPyo=>m=cAf+@7^Md1D094EeynghLEGK0MEz-+%-YCh zDya<+iOAZbqZA&lPfeum(s!k0xu&{`A(wlOz-(^L*XYJJ`0$dIkj8oJ8v0J=X3jye zv5)x;kx8M}Y3x~*sTBu&-^V1Xv(`tppP4AFOH~D!)v|kv@@y6e=7uKcpYj`wo(t+d zKobG}W!6oU_8h+Jfya90a3hj0o1PqgG(_M`H8~e6m+EYFidtP2ONMZ# zXm0wbrRSv0}*Tyia}~-EQ|dT|G-;**Xz0#(4kEm z@siq7fqWSA`x9RP(-~dNA|CFMM>V)iF1G>+%!bg$7b26A-S>|vXp1dqyCc7Wvle!s zjr=gwto|I)*J1Ewzs|G@$U3z4YH8irz<6#&E9XJhK}DpBR7C8|fssV3we2~J%f6mk zF&40tgY`6H+0LAAhf^*C>rr@VB$rTr`#;eR*LDT{J9#QAponnFiX#p&j?Y&#Z{2x3di3T=OPG}Z2+Yq49rthn0O|^5xXi5qzlPLF zpz(UB|HQ=lyihf3S6t7nAG321X>DL0e{moWX*7odO;17HA+h@j<91UYhAL$29cRvz zyqc>znP%d?4pPW9$A$7_tB75vx^eR(I{Z`Uxtj>wqtEypd4YUd&c+?yV{$NQGBRZE z0-4H>TJwEif9nS`sGca}0RoW<67L_Gxw+z1k_U?Q7QEEL$Aa)4h{c$pa-e_Zq3tEu z@=A+Vb{V#$-j2_LBm6lW;h;-9a@qyf(zL3=NE(8@+0zr>Ge?pIY|jp$ee_*Y`eM?o zH1p&8jO?-D97Cw!O>GFA=AdygL-c!;h9n1y*1%!o^{mSe4hR@?IJyZgnvl`w+<%@X zTh8+)tgF)*u==H5vp%wAEpX0KPk*FEdV%Bp3OB2^>CC_U$7U5$-7l2rp}vGqtQ^Ui z&2LyIrGM4vJeT|asR^BJ5yi2P>LZ=GmUZNuhmaqfr)!B?aq0Y{e8&rQg?-&nDK#!3 z_02sA-Fu*Zp{s+v82bs9?nYvEo6XLdb=AO@pWHxztZ3`->0~AkK(q)TThW9K`w0mkIq4sb7EhM?xC*d zUR-*fN5SNOM{%M*6al7w933s#zIB^KBzdx~cUAfz7hH6Uv7$``ER(K6k6F>fYVuR!AQqMm$M~p?~{#S_J9K-%d?{sxJT@|KcO#sH;N~X!+~UC!|8E z%`5_YneZ3DGLQ)|Ima<9#y%suHq&_rmr+_SGPiGxXv^8V@QMm zP`-C+g6uV)vEoVHdde?lK*B>W5*`By5A(f>+gh;_mcEp-W*hamk8?}Afwi13v;Nw% zz#p0rD5LL(f~&4XV9BtP04r19zcU5AKX3sTl@q`hJn~#u6Cy9XYnL{2CdT}orzS9e zgb0U)S1`t3Viq%HB(ReI4Bwiu@RJlfb)1DzT;{>gUcCRTX+M`Crx6u|6?x$LuIZiyG4>zp>L(S`cOU~?HIkhAKa&A2&qFZls!)~AQD3SJfDDe`>6K9S zy%=>p3kZRV6dtbtae{-Atd4{f4g?bz0b-F8SXs0@aXiZfgyMRFFO~W3WSutzXvbp; zPqTw0e+eAPL8@DrbQ`t?c(!7lv(jwB)P0xAQmJ?)EXMOh`i@<(&BZ0Hr*!85Eef|r zo|pqE2DC_O1J8f3j}eNa9W{uDQ<+1{zBV0N_H#&b>=z11J=KwzB^9wMzKD)xP@JVv zJhd5=15czM!}a8np5z#ov+O;jbKexu5_Ad<+nij1Rk%IEvsC*EwVd9bVv--eoDxD= z4ZFn5BLP0#GGJP~|08g zXrM^ON=HP8Co`_i%=9EVi$j6R9!^rmR)4hi723WCi0jR315OZ{Y#8{RH{+k%vwUM% zLd2qTM|Uyz0`8=)+2402x&8^JErNa;uYOwASG#bZxt7E)?z{lxM~TwR)1_*vmL&kW z>HYM3qgZb6-=O=4&ZW79(|xKwpK8FF%SESR%2}XkYb=jzR&;XP95C#?+ba5+%TCvo z-NBK#>GN4FRepp2gII&yFR-l3oMV4a!}?X*FyUTw4~%X!HU`qS5d1mJ{R#c&@RIsy6p+{cIDko8#AQYb}c*5nGU zEs_cis+tpkmqLB!*RxyK(#_he&8d#qSC|*M`h@=e!dY;1n8-r@^eS^iaG8rASzs3EK~&gsOl6UEqlab;n%Yp@}|A0PeZF%q}Z|4x~ePkXHyy zG^Ob0(0G?You}+hU~OG0&0)Or3ac_n%26wY(X?UugQ)0T$hPN0u_h|TJFh#vT5&I6 zs6rG+fY4N`?~>6LHy%jl1w6}G|GF(oc_Wj67z`V?4*7&{DMB&RUcgM)VT0R~ko02r zHYl5bSKd-R1BxeD3Te!Y!;;ac?)HON0);nxhY5eF-##6j8VDc9V7U%JJr-F(WXK^S zHt0kx=~`F`T{*r&85I1Yi+%Ju{5P~AJqu+LG)95_UGZMMb<_&anXQ~OrT=AP`x^_G zjTr7JwJ@YIsxQ%Ca(NIVfo+sO4DBU9=Q_Ve^ZlhB#qzTN?cYmm*^s^?kfdUBjM4Nr zweq3=^DD{Qv#g)hFuw|sp)FvOx6y5_T98|WFGfimz|$4oO}j{wp4oQAc@fjpcP}%Y zVBRHO>Wxv`Q9+0S;d5k0Z9+lF5aIJmMk~-FR~-%GcKXrUuSl3AZ(OfF5T(rG$}gj=-B&z5fG$S=rJs__BNKl*U%BJR`du+}{FNsFi_u3Nqzz{N zxmQVw(I+q+K#V>xI;kM!WCh}C11%*XYhFVuHw*>5t=>n;(u{rK_?t{BA@_maA!VL8 zr6(o$zRPfAe=4Oo@B!!9_^XcG!!W)K)Cj4a+0hqQ{c_dOu|>~UozOV*;DMRjJI*9)XT*DRRnDC9 zU+VeSf(Mq5t56R4pO|+9GJ^afX3*;n;SY3Q36rgj;JxC#@%x3THTheE)+*%XnBNIy zj)uHJY|%OJH|&ZF!Xz5(i%_L{Z==4gD)CZP`VM{nKlCkThBgODFoyWnE`drL_G^vZ zsd7iEa!@u%jW*y%rYdiq! zX@9Bw(&RXsu`Ay;uX{H2CH}vtI#X7+spn}~-_y~@k}^*vSsfJP2=Y>aod}dsFWVyW z2X>F33c5Wd&0CqcCDMCN+OBI*c+bS!a&`SD@rPxnH5s-Bi?OSLqJNyxpqJ<4Ix(BS zqun%i_h*;im(qVgeuOI4^%rT|Wt6=D%_#8`xvRanS=Ug^cy4rluA!m{tvXHPF45HG zl%LMA&e7Qatf?|GUSrT8&(JG)+4|bj1l4_XXYCTzvrWD0sCSvAls#Edub?l+;}4fV zN%hpHR8qw$jBAQziJVDC6J!>|I#T7i;i^61t!u$HV{>$jTW9*1$ucZVyLy|&2P9ed zplKu>-PW8{+_Yw@<7}E#t!ivd??0hAs;g;l|Ai#WvQD|P)4jWFbBG~XuGM~l zf292Kq0^^NpX=!O>n^$Xf~QjM8OvBC%onx$x1fx^25eFz9_YR#2Kh$WJ(#_H_&b@N?bn@VuXW_5MXH>U{%+Xp*|S^ln`N(&Wh(KKS4#*i)!L)^F?E@a zvo$&czG!0eO^Ivk>mQBqoC_PfY~{*BCp7fR@e+v(f4#dDBzAjW^nbJeQ}C9CW$i~M zj0t>EVR6&p=H}adt5-F{u&{E=^rZBsnoOBZ+1b(OZ;w8fmSsv?(OFpBqqR!lV;)u6 z>74Rg#(hMQ7jO{wWSC}%cxZo1qy z{d@b`wr_s|wx;_P5zT>HKZPUAMi|9a;a30Zn ze!qLo0s4HN@l|H2k+#L1YsjLlo;dqs=HAE3TB9l^OM2gv!iN|5(o0$NeSLiz?@P4#jzXpzrEx>h5wH*6d1KGlhIZSU*{!^K-LargHFW zFV#1_j$fMuwoTQd)nzq@XRS5%7aCjShtZ0nDq5p@W@~M;%RWSdbZHnR`3M}z#6Q6M znh%jrD4Ko0bp~8jJ$!AtEs{#b6Fc8~D#d#wHc7<&?~?rdS)v7nwar z%^(&P7EJoJsaGKm+H?opWqP*-qWHeJ3c1V&&U zFFz4gexup>hWX2}-VQZ=r@Uvf{NU7zmZ?3z)%5<>lkCA&R4{ghUUw1>D?F8^5R=Dk<~kII91Ia(=lpM>mtK|5)PG6+L$-4w8k%I71Z@z(jDp!5J!^ zQyW4Z*^-MtbZ<(@nn;&sy7lt4DQ;JdO@1;8%)bYtQd|d+zM_$TM34oZ8I+i z*FIGh z%XWAJ57mRQg#;wYO8z$s^b7kh%8ge8q%qH+t3OI&%fZGqorM6l03k%bykW!jiEo1M zBiI;ET-lw8o+Z1Q2-iLSa%=cy~fsJJu`@$tR)88K5??uQup z8a3lon+Q%yP|$6PqXe?N=?7*I705RbXqH)((Z#s>=O2oFnSeshoLsZ~ zkjwujW6uTt2E6@Kix;mmt>zn6csn{R5_svUWoH}8C&^wzoD?`-(ZznmiZK`t%(Y+m zF%^P{nW^p9>GcN6Yp|h%^-z0CN@_8V?%lKJz}OfJB+4RE1RG~9G@Eyc< z!5*8VonNxDq6A zz6QZOXhRZz7An!TX|0HT$kIxB5qxH}CuRL2L=H;t;)9h}6wV(aM7i!St>s2{IUa;L@HiQ!w5%y$WxA?ypMsf@_dvu# z-UDNj%c*A46W=9oO_;5x2)>=3bQJpH@Tp0nQLTpevg~yJJ+A-fb8j z>gpd!dU@f4Gc5F%HJOby7?sD_aKkXPR6PJVfrnnp?gT%a^^$*MlS5+8oEA(-oREYs zi|By7Tya@l$j9}bBl`2c#Fuxd%&vTMEoP?? zA>D(f*%u&K>g$ zR5wdjg;#OW9eRZPiT6cumR|U5QxmfQtJ)U1HMp14n=N8ZLURYWzQ#p(5ecPsz;_| zGf-0>%Sp&SCP>GXOU?3e1nF1N-{m)CXMCBx%$Vu0?p$MZWGV>Bz}r2l){$j)U?>}# z`DJ#-HE2@ItwvA15#U`?`VJ0lZX}F{4@x82B)&+|iXM9wATS=3kiDjXRQ6m6$5~sPZOV0eDqRS-JGd!AU4pOaB$8u?nu9^$n96wWb z4=)_CY#m+H1fV82Qnfd-buADS6*(Hlr7)Vjgd z^gIV<7JZByU`*~kaTZ=6KuGIBbj}*!-h;E~J}3``iI zhP9tR398@@Qk`LL^!m6*79!9^Kx0+Bgbtw zHCb6U^SCT$VT;mjVtyQQwd7}bTqc_=Ewr2W z!l@=^NO1EpB;BmY-UzPsGJ6EpqD$$gv`6x?ED|U^YqPovTy+ZX3-pwJ9v`W=!1@=S z!%hf^AY~y-XTzl0c!+{$4_J1~2OwK^56?ev(M4-#!Mmil0n)CFvxdw`%tiqSF1#uZ zcza9WG_c&6at9$v=K^2q%;k^>KQReOs(9w{Tr37QglQ!17`s9hO6m8oe1)lJGzVr~ zx=ZXf&{2d+Ca_9y0w@)B$KGHG;7)#^e~0&ignJ_~M9~@Y?d}L^Im4tTxH<}t&h;@| z<2cfQR|jGeLV6q_hC37(P1S}(yPT9Rd$5SnvN0wObWEgSj&d$ zW*J6agVgE>9#0_L2MHTI2)#3%jXG2gV;f6_2!}McTvbRzN}SqWo}=fs=sJAd&dt1CZ)VSn@LiuN?s|S`>?1IQ1noMhI|p)AI)-rAgfW#L4jFTjuHac0mT7G!N7HKs5V0m981<&tT_u8H`QDfag(NlSFWPo4^^#& zGI;2mF62TabK#Iz6$_zhWIhU+80ZeN;Q=;;)e#I9>O=n85NdGzghpP+Ih$l;GfBya zgdz|=xr*1ImnH%r@Zvf{$k$F%p$3G=*@$e)coAuhdAiuV#Z{~NLE8^vcT6@UsTu4YBy8459g_i*kE|m}(YC^3M&?O`{9$E&P7cYG|@zAF6XofOg%^NbJe_{#;u`{?{sRT zkO^*s=+8ZIy*C1T6J1;k>`klgD;}4^>cTw9A?;mvHK|XIYfv=vs*5a6qNj#_hLANt(%CzD_Ud4KJ{2eGK#xOsOlYBKgN!XLjmE1eA+KqA^A8CgHFBCIPyfSrGrvmBW%Zjf0q8*hhqFzHd`8Bpa7ggpr*_XonX2%Uz3{4@ zY|gTa{G5;#G(bf6`wG8MiZm|@{lZYpj0=J5}{Hf91~q2!HsY)C07!5 z<>_V-$(c{y*D9x5T?Qx7yI zPe`lWL9}p$6EpC{T%5i$*}VvQ4r2!$vU`?7R%_8HOJPa?=aKd=60WgG-ULm zzTp8rR)!_GFOyEbN`Mdbb?T^NG|_>Kb5@nrna*JQ;j zIuoG-ijE^X&{j#xtveqaqDdG8#2#M7nE_w5L%3&>rKh^zl(>F^J{)xY;Oy%1%D>68 zqB^mxMpYIj``BOfXVnrp7cn`^gV3M8lvRIiyi6KUjZ)$YPn~WXQ6t7QwZ=Fd`~b?5 zj?Dk?42OflTE8I@w1K=*L6DGm4y%t5d~KuGAZ$q7pot^55S+p@Xwz9mAK#d;VGjD= zoV(5acN40OszvPyiZh^b2ANi^d%w2z5qqEz6Sf~4gHQ^JQqHIFw4>GqX|^4Elk`0Y zq@Du{?FYYIs>p?;0{-UXx01r)PG1WLHkFYEJNQoNk(`Sd{!lB-!}gC@N$X7S-z2Cd zqc!E%Q{0Of_W+f=>Rbc%VDySjH2Xe994F7^) z5zp1>Ym%SjLO?NUbyA$Krkul8cV!kD1jcCs`%J6hEy5^E7^>WZ+yfr_fYhCc0}zzhj>cb_Z0ERX!1&I z3gpW*c>cSQ7(NC*bj;Cmq6TXA=+GHZ#X4sOL__Kk0EtV3o?l6Y5&myiv1tG3RfNn(6iQsO%p6tdQ zura{nu+cQjM|q-%74lZ>{>2C2{VkULS!kCHp?S;4&#fWk3HmOR30(^chOuP254!5V zmyS&|q`L;Yq#y&!Z#aJZ2BLSvN~$rwXg z90rP9N#_r$=e`Mi3;u1A24R8@0xSfeSs8dJArB11Q0Xh6MhvA7G9NaKZ37B6TD04twar+!3`a4rm$kH!e^G zJY_LgqPLM=96Y4{{%*-%;9E#bwFMnLzErdv0G9M1E++E`r{ifPx@ZEdg?ntFvdb6u zRMt+6sH2M4<)nQ=HIJvK5GgXIZzy)x8F-j|puUAbV`Nov>lB@EXeIPHNjm@^st}hL zg?4DA=H=MvjUB6C94k)hF11A{p;^Bc}H zN}u8>Q{eU#JcJli{TMp10`kM0R!gxGwd17W$%kQ4FD0C4QVGf+R-L5YAr3jfZW=Fg z>2zp?E8AWHm#RZMP@u43OId}_kKMkcYrxooXOY7ZaA8O&=8no{*&E?i6RZ`97Z6Z<5c(_ z)g+k7Ksq_GPnmg(*e!N>J`LcHD&*~74CznyTd=W{T`j@fPyszv5GM#*^*$UCq69|R z_zXN*IC-J8OhXzOapOrY=h}rxb1(KU{`d=mEiiiG^2kU9mM~?$j%yUnk$e#;0&KFp z6Ze%g%435wPPyvi35_{r=OVJ~*m^{Kt?Xlj>7=lS-JxXd38Eqi7fLNQl66F6fe|BQ zbGfgrAJxu=tS&?QBdhu@{h3HZiF1=5CXN|j1^I>WljvbdLmHWypvBNgm&EgM1ldu6U<3LOwj&|x=M(5 z1TB$@QlLE5xU}5^|aH3_K?oWV5gzkSJ7orcgfACS3x)iJUqT z?l7K{Fio;Mf(iUV83YW>1acCH!ok-aOxnYB4J6?vK$`p-e?$sGaRNw)Da>sEQ3RP0 zfj{IM4uhjj2-kr7Lr{u4l;PIuYA|Ia)=OXu{*=B8dLM{Pk327t{AvhtRK6d-<__`$ z7(qx$9svq|91k`5IQ1Et2zhyb@%J*W$`1xw5p$EN9E9aUP=lcu700^s_`L*^7o+3@ z-;VS}Ao{^4CT$?W?YD=?ijb)TTtgGTpOnfbw0^&Gj|Qd`6L?gE+!kYE}1b33?Iy@ zL4Vt>Y2z_#M5aNe#P|nC3EEAjbSSx0-FxF2UA^*l*!F+ba>exCoAR57+t%JUCBM(9 zXoFvtJZMN%6_#AoHC8GvJ*r$PJ{0Pg8KbyTT;un*{gdF8!qk$q{*pd0GWvv^cWN_w zPC}SSR$}IZP*iaB2~i4@CTODaKxZf5z%#!Se@Vb{4G2Br>J3;BLH{bQo}lF#@ZtZh ztM|&@xb}mYaez5S;P!;4?L$U%A+k*B{h@%V@T0L?#yMiPjOe{~_iI-Qnn^d3_j*Dc zTD_@cnQ@a~g+Q?7{(B6q9)?*8=Eg>1jN&1C=I%zvGr^`Mc+|1g%Z#LUgWa0NjzlYAp??cFef8Mvf73F%(cVH`MF~$9y0NeiHGdm(Ot~F zAuduaUDhWavTrFhh&h>!tCHb(0#Q|YTy6$Y}48gp06s5^E?XQ$NXevSCL_B0bLj4scL%L#J^xY_-}v9 zTU`EJy00FR+FpJ54-t2!wmh$9o)CXY$YP4CC&<_ad{|t)0ShANU&YlEv|Ix|EUw;w z1rhYGqiXe5UpQE}t=zqQ#2ihYwC{x@?KSB;>=%DpEVoNQ=@eXracLV*B5Oj38O9At!t4!RURZ%&1 zn)>c#rW4G&U^DU`EK+q9wSUa4D2~1~qr=n?eXOVB`)RRW^@z)`5^7ImTgjwuW8K+e z?+=5xCsqf3eL>7p)kX7&^T!DK>6F8%JTi;BSGu&XI`?U}b3=6TE>ERQ@Bq32nd%ca z=k3~OJ)k`l`p2S_^^<2R+i!?2j!N;~xH;Q2(p9{+CNAdX6NZ>qO{-0X)WIGL+&ycj zuG!yrCb|9zrfv0OjfZALmi=76gmOIAU2aPe+#C~}mN3_X8JxUeii$37(Db|i5w8Nz z&aLIt_gimQXN}EY(EjC#2qt-?UjOrNGP1$(UyLq(sz|>i&KrXn&@KL4Yr#TQ>%2Xg zo?l-z$3J}$RadvCBy*a+t1(8M!7{Tmv@)#l)CVd8cqYs*h9w9AiLAD1+rpP~M*og;P4Z^3?-s3PRTrhU{Ni)9@zB$)KOdnG!MsPOnt~_mlQapqVdf`(H{80)`=h)D!&mlk{jTw2 zayD!lp^e-2M(-c<#V2PL-?sUZx#_{=$@pWp_QX>bIO1-$%ry5|=8l0wP)$(YlD% zg|e9LeK$j zWbl6#mum2}8DMVWQi)6DUjZ?(8E`fR|5tIT249;1Co5XjkWrqvRN_(vuYg$G8sJ6- z|5tIT249;2Ifl4Y;!*`xKqO&;J??;`!v9xssl=u7uYi~?i|Mk6)`#p4#HA9KDtHA% p2pRxjS9GM}QvKU6l~v75o89q2V$!ZL@ISwO= 50 && i < 62 { + rps += 1100 + } + cpu := 14 + rps/5000*60 + (rng.Float64()-0.5)*6 + lc.PushSeries("cpu", math.Max(0, math.Min(100, cpu))) + lc.PushSeries("requests", math.Max(0, math.Min(5000, rps))) + } + lc.SetSeriesRightAxis("requests", true) + + header := lipgloss.NewStyle(). + Bold(true). + Foreground(lipgloss.Color("#c0caf5")). + Render("CPU (LEFT) AND REQUESTS PER SECOND (RIGHT)") + + content := fmt.Sprintf("%s\n\n%s\n%s", header, lc.View(), lc.Legend()) + + box := lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(lipgloss.Color("#3b4261")). + Padding(1, 2). + Render(content) + + fmt.Println(box) +} diff --git a/docs/axes-and-grid.md b/docs/axes-and-grid.md index 45376d3..7896388 100644 --- a/docs/axes-and-grid.md +++ b/docs/axes-and-grid.md @@ -28,7 +28,7 @@ fmt.Println(chart.Min(), chart.Max()) // 0 800 Throughput chart whose range follows a spike

-- `View` computes the range from the finite values in the visible window (the last `Width()` points) of all series, the default series included. A spike leaves the range when it scrolls out of the window. +- `View` computes the range from the finite values in the visible window (the last `Width()` points) of all series except those on the [right axis](#right-axis), the default series included. A spike leaves the range when it scrolls out of the window. - Gaps (NaN, +Inf, -Inf) are ignored. - 0 always stays inside the range. A chart of positive values starts at 0. (On a [log scale](#logarithmic-scale) the range is in whole decades instead.) - The bounds are rounded out with `scale.NiceRange`, so the top and the bottom are tick values and both get a label. @@ -67,10 +67,45 @@ chart.PushSeries("p99", 300) - `WithTicks` works as on the linear scale: a tick at or below zero, or outside the range, gets no label. The label formatter gets the real value (10, 100, 1000), not the logarithm. - [Thresholds](thresholds.md) use the same mapping. A threshold with `Value <= 0` draws no line and colors nothing. - The fill goes down to the bottom row. -- The zero baseline, the symmetric range (a fixed range stays as set, for example `1..1000`), inverted series, negative styles and zero-crossing fill are ignored on a log scale. An inverted series is drawn at its real positive values. +- The zero baseline, the symmetric range (a fixed range stays as set, for example `1..1000`), inverted series, negative styles and zero-crossing fill are ignored on a log scale. An inverted series is drawn at its real positive values, except on the [right axis](#right-axis). - Events, the time axis, gaps and legends work as on the linear scale. - A custom renderer gets a `Chart` whose `Min()` and `Max()` are `0` and the number of decades, and whose series values are `log10(v / lo)`, where `lo` is the bottom of the range. +## Right Axis + +`WithRightAxis` adds a second Y axis on the right, with its own range and labels. Use it for two metrics on different scales, such as CPU in percent and requests per second. + +```go +chart := pulse.New(80, 13, + pulse.WithRange(0, 100), + pulse.WithRightAxis(pulse.Axis{ + Min: 0, Max: 5000, + Formatter: func(v float64) string { return fmt.Sprintf("%.0f/s", v) }, + }), +) +chart.PushSeries("cpu", 42) +chart.PushSeries("requests", 2100) +chart.SetSeriesRightAxis("requests", true) +``` + +

+ CPU on the left axis and requests per second on the right axis +

+ +- `Axis` has `Min`, `Max`, optional `Ticks` and an optional `Formatter`. Without ticks, `scale.NiceTicks` picks them. Without a formatter, `scale.FormatTick` formats the labels. +- `WithSeriesRightAxis(name)` or `SetSeriesRightAxis(name, on)` plots a series against the right range. `SeriesRightAxis(name)` and `RightAxis()` read the settings back. A series flagged for the right axis stays on the left until an axis is set. +- `SetRightAxis` ignores a call with a NaN or infinite bound. A `Min` above `Max` is swapped, and equal bounds widen to `Min..Min+1`. The right range is fixed and linear, also on a log scale. +- The plot stays `w` wide. Every line of `View` gets one cell for the axis line plus a label column as wide as the widest right label, so all lines have the same width. The height does not change. The time axis rows keep their alignment with the plot and are padded to the same width. +- A right label sits on the row of its own tick. A tick outside the right range gets no label, and when two ticks share a row the higher one wins, as on the left. +- The horizontal grid follows the left ticks only, so a right label can sit on a row without a grid line. +- `Legend` and `LegendBox` add ` →` after the name of a right-axis series. +- Values outside the right range are clamped to the top or bottom row. Gaps stay gaps. An inverted series is negated before it is mapped, also on a log scale. +- [Auto range](#automatic-range), the zero baseline and the symmetric range use the left axis only. [Thresholds](thresholds.md) and threshold colors do not color a right-axis series, and neither do negative styles: its cells below the zero row keep the series style. +- The fill of a right-axis series goes down to the same baseline row as the fill of a left series. +- A custom renderer gets right-axis values mapped into the left range in `ctx.SeriesData` and `Chart.SeriesData`. `SeriesInverted` is false for them, and `ctx.RightAxis` marks them. +- When the time axis is on, its axis row ends with `┘` under the right axis line. +- Both render modes, events, the time axis, gaps, history and `SetSize` work as without a right axis. + ## Custom Ticks For fixed ranges (e.g. CPU or RAM percent `0%..100%`), you can specify explicit quarter-step ticks: diff --git a/docs/bubbletea.md b/docs/bubbletea.md index cd36ecb..b9b69dd 100644 --- a/docs/bubbletea.md +++ b/docs/bubbletea.md @@ -69,7 +69,7 @@ func (m model) View() tea.View { ## Resizing -Terminal size changes arrive as `tea.WindowSizeMsg`. Call `SetSize` with the plot size: the window width minus the label column and the axis, and the height minus the lines you draw around the chart. The data stays in the chart, so you do not create a new one. +Terminal size changes arrive as `tea.WindowSizeMsg`. Call `SetSize` with the plot size: the window width minus the label column and the axis (and the right axis line and its label column, with `WithRightAxis`), and the height minus the lines you draw around the chart. The data stays in the chart, so you do not create a new one. Create the chart with `pulse.WithHistory(n)` to keep points that a narrow window hides. When the terminal gets wider, the chart shows them again. Without it, the new columns on the left stay empty until new points arrive. See [History and Resizing](getting-started.md#history-and-resizing). diff --git a/docs/development.md b/docs/development.md index 55a9e39..3d0023a 100644 --- a/docs/development.md +++ b/docs/development.md @@ -47,7 +47,7 @@ The PNG files in `assets/` come from `cmd/gen-shots`, which prints one chart per go run ./cmd/gen-shots -mode xaxis | env PATH=/usr/bin:/bin "$(command -v freeze)" -o assets/xaxis.png ``` -- The modes are `hero`, `quickstart`, `styled`, `braille`, `rxtx`, `events`, `fill`, `zerocross`, `xaxis`, `xaxisat`, `autorange`, `thresholds` and `logscale`. Each one writes `assets/.png`, except `xaxisat`, which writes `assets/xaxis-at.png`. +- The modes are `hero`, `quickstart`, `styled`, `braille`, `rxtx`, `events`, `fill`, `zerocross`, `xaxis`, `xaxisat`, `autorange`, `thresholds`, `logscale` and `rightaxis`. Each one writes `assets/.png`, except `xaxisat`, which writes `assets/xaxis-at.png`. - The data is generated with fixed random seeds, so a mode draws the same chart on every run. - `env PATH=/usr/bin:/bin` hides `rsvg-convert` from freeze. When librsvg is installed, freeze converts the image with `rsvg-convert`, which ignores freeze's embedded monospace font and draws the chart in a proportional font. - `freeze --execute` hangs in a non-interactive shell; pipe the output in instead. @@ -77,6 +77,7 @@ git push origin v0.8.0 | `renderer_lines.go` | `ModeLines`: box-drawing curves | | `renderer_braille.go` | `ModeBraille`: braille dots with Catmull-Rom splines | | `logscale.go` | `SetLogScale`, the log range fix-ups and the plot-space `Chart` for renderers | +| `rightaxis.go` | `Axis`, `WithRightAxis`, `SetSeriesRightAxis`, and the mapping of right-axis series into the plot | | `threshold.go` | `Threshold`, `SetThresholds`, threshold lines and row styles | | `event.go` | Timeline events, `EventsBox` | | `legend.go` | `Legend`, `LegendBox` | diff --git a/docs/getting-started.md b/docs/getting-started.md index 4e08057..524e8c8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -168,6 +168,8 @@ if val, ok := chart.Last("cpu"); ok { } ``` +A series on the [right axis](axes-and-grid.md#right-axis) gets ` →` after its name in both legends. + ## Dynamic Settings All styling and rendering options can be modified dynamically at runtime: @@ -179,6 +181,8 @@ chart.SetAutoRange(true) // fit the Y range to the visible data chart.SetLogScale(true) // map values to rows by log10, see axes-and-grid.md chart.SetThresholds(t...) // limit lines and labels, see thresholds.md chart.SetThresholdColors(true) // color series by the thresholds +chart.SetRightAxis(a) // second Y axis on the right, see axes-and-grid.md +chart.SetSeriesRightAxis(n, true) // plot a series against the right axis chart.SetXAxis(label) // time labels under the plot (View adds 2 lines) chart.SetXAxisAt(label) // time labels at the columns you choose chart.SetFill(false) // toggle shaded area fill diff --git a/docs/thresholds.md b/docs/thresholds.md index 2edeee7..f4cf9d1 100644 --- a/docs/thresholds.md +++ b/docs/thresholds.md @@ -52,6 +52,7 @@ chart := pulse.New(80, 13, - A threshold above `Max()` colors nothing. A threshold below `Min()` colors every row. - The color is chosen per terminal row, in both modes, so a point in the same row as the limit takes its color. - An inverted series is colored where it is drawn. +- A series on the [right axis](axes-and-grid.md#right-axis) is not colored by thresholds. ## Range diff --git a/docs/zero-crossing-fill.md b/docs/zero-crossing-fill.md index 0713e28..4c5f511 100644 --- a/docs/zero-crossing-fill.md +++ b/docs/zero-crossing-fill.md @@ -14,7 +14,9 @@ This capability is essential for modern terminal dashboards monitoring: --- -The zero baseline, the symmetric range, inverted series and negative styles are ignored on a [log scale](axes-and-grid.md#logarithmic-scale). +The zero baseline, the symmetric range, inverted series and negative styles are ignored on a [log scale](axes-and-grid.md#logarithmic-scale). An inverted series on the [right axis](axes-and-grid.md#right-axis) is still negated. + +A series on the [right axis](axes-and-grid.md#right-axis) gets no negative style. Its fill still goes to the zero baseline. ## The Problem with Bottom-Anchored Fill diff --git a/legend.go b/legend.go index 40ddcc2..4b0a512 100644 --- a/legend.go +++ b/legend.go @@ -11,7 +11,7 @@ func (m *Model) Legend() string { parts := make([]string, 0, len(m.order)) for _, name := range m.order { swatch := m.SeriesStyle(name).Render("■") - parts = append(parts, swatch+" "+name) + parts = append(parts, swatch+" "+m.legendName(name)) } return strings.Join(parts, " ") } @@ -28,7 +28,7 @@ func (m *Model) LegendBox() string { if v, ok := m.Last(name); ok { value = m.formatValue(v) } - lines = append(lines, swatch+" "+m.axisStyle.Render(name+" "+value)) + lines = append(lines, swatch+" "+m.axisStyle.Render(m.legendName(name)+" "+value)) } return lipgloss.NewStyle(). BorderStyle(lipgloss.RoundedBorder()). diff --git a/pulse.go b/pulse.go index c8905c5..59b19b0 100644 --- a/pulse.go +++ b/pulse.go @@ -51,6 +51,9 @@ type Model struct { hasNegStyle bool inverted bool + rightAxis Axis + hasRightAxis bool + thresholds []Threshold thresholdColors bool } @@ -62,6 +65,7 @@ type series struct { negativeStyle lipgloss.Style hasNegStyle bool inverted bool + rightAxis bool } // New creates a chart for a w x h cells plot area. @@ -274,14 +278,17 @@ func (m *Model) LineWidth() int { // SetTicks dynamically sets explicit tick values for the Y axis. // Ticks are copied, sorted ascending, and deduplicated. func (m *Model) SetTicks(ticks ...float64) { + m.customTicks = sortedTicks(ticks) +} + +// sortedTicks returns a sorted, deduplicated copy of ticks, or nil when it is empty. +func sortedTicks(ticks []float64) []float64 { if len(ticks) == 0 { - m.customTicks = nil - return + return nil } - cp := make([]float64, len(ticks)) - copy(cp, ticks) + cp := slices.Clone(ticks) slices.Sort(cp) - m.customTicks = slices.Compact(cp) + return slices.Compact(cp) } // SetLabelFormatter dynamically updates the Y-axis label formatter. diff --git a/renderer.go b/renderer.go index 41230ca..5ed692c 100644 --- a/renderer.go +++ b/renderer.go @@ -28,6 +28,9 @@ var DefaultTintColor color.Color = lipgloss.Color("#1f2335") // // On a log scale, Render gets a Chart in plot space: Min 0, Max log10(hi/lo) for the drawn range lo..hi, // series values log10(v/lo) with non-positive values as NaN, and ZeroBaseline, Symmetric and SeriesInverted false. +// +// A series on the right axis comes with its values mapped linearly into the range Min..Max this Chart reports +// (the plot space on a log scale), negated when inverted, with SeriesInverted false. RenderContext.RightAxis marks it. type Chart interface { Width() int Height() int @@ -68,6 +71,9 @@ type RenderContext struct { SeriesData [][]float64 GridStyle lipgloss.Style ZeroRow int + // RightAxis marks the series drawn against the right axis, in Names order. + // Their values are already mapped into the left range, and threshold and negative styles skip them. + RightAxis []bool // ThresholdStyles maps a row to the threshold style of series cells in it. It is empty when threshold colors are off. ThresholdStyles map[int]lipgloss.Style // ThresholdLines maps a row to its threshold line: one rendered cell per plot column. @@ -181,12 +187,14 @@ func renderGrid(c Chart, ctx RenderContext, cellAt func(r, c int) (string, bool) // styleFor picks the style of a series cell in row r: the negative style of its series, // else the threshold style of the row, else the series style. +// A right-axis series always gets its own style. func styleFor(owner, r int, isNeg bool, ctx RenderContext, fallback lipgloss.Style) lipgloss.Style { known := owner >= 0 && owner < len(ctx.Styles) - if known && isNeg && owner < len(ctx.HasNegStyles) && ctx.HasNegStyles[owner] { + right := owner >= 0 && owner < len(ctx.RightAxis) && ctx.RightAxis[owner] + if known && !right && isNeg && owner < len(ctx.HasNegStyles) && ctx.HasNegStyles[owner] { return ctx.NegativeStyles[owner] } - if st, ok := ctx.ThresholdStyles[r]; ok { + if st, ok := ctx.ThresholdStyles[r]; ok && !right { return st } if known { diff --git a/rightaxis.go b/rightaxis.go new file mode 100644 index 0000000..e8376a5 --- /dev/null +++ b/rightaxis.go @@ -0,0 +1,174 @@ +package pulse + +import ( + "slices" + "strings" + + "charm.land/lipgloss/v2" + + "github.com/ingvarch/pulse/scale" +) + +// Axis is a Y axis with its own range and labels, such as the right axis of WithRightAxis. +type Axis struct { + // Min and Max are the range of the axis. + Min, Max float64 + // Ticks are the labeled values. When empty, scale.NiceTicks picks them. + Ticks []float64 + // Formatter formats a tick label. When nil, scale.FormatTick is used. + Formatter func(float64) string +} + +// WithRightAxis adds a Y axis on the right with its own range and labels, as SetRightAxis does. +func WithRightAxis(a Axis) Option { + return func(m *Model) { m.SetRightAxis(a) } +} + +// WithSeriesRightAxis plots the named series against the right axis. +func WithSeriesRightAxis(name string) Option { + return func(m *Model) { m.SetSeriesRightAxis(name, true) } +} + +// SetRightAxis adds or replaces the right Y axis. +// A call with a NaN or infinite bound is ignored. Min above Max is swapped, and equal bounds widen to Min..Min+1. +// Ticks are copied, sorted and deduplicated. +// Series on the right axis are drawn against its range; the horizontal grid follows the left ticks. +func (m *Model) SetRightAxis(a Axis) { + if isGap(a.Min) || isGap(a.Max) { + return + } + if a.Min > a.Max { + a.Min, a.Max = a.Max, a.Min + } + if a.Min == a.Max { + a.Max = a.Min + 1 + } + a.Ticks = sortedTicks(a.Ticks) + m.rightAxis, m.hasRightAxis = a, true +} + +// RightAxis returns the right axis and whether one is set. +func (m *Model) RightAxis() (Axis, bool) { + a := m.rightAxis + a.Ticks = slices.Clone(a.Ticks) + return a, m.hasRightAxis +} + +// SetSeriesRightAxis plots the named series against the right axis, or back against the left one. +// It has no effect until a right axis is set. +func (m *Model) SetSeriesRightAxis(name string, on bool) { + m.getOrCreate(name).rightAxis = on +} + +// SeriesRightAxis returns whether the named series is set to the right axis. +func (m *Model) SeriesRightAxis(name string) bool { + s, ok := m.series[name] + return ok && s.rightAxis +} + +// onRight reports whether the named series is drawn against the right axis. +func (m *Model) onRight(name string) bool { + return m.hasRightAxis && m.SeriesRightAxis(name) +} + +// legendName returns the series name for a legend, marked when the series is on the right axis. +func (m *Model) legendName(name string) string { + if m.onRight(name) { + return name + " →" + } + return name +} + +// rightRowLabels returns the right axis labels by row, and the width of the widest one. +func (m *Model) rightRowLabels() (map[int]string, int) { + a := m.rightAxis + ticks := a.Ticks + if len(ticks) == 0 { + ticks = scale.NiceTicks(a.Min, a.Max, m.maxGridTicks()) + } + format := a.Formatter + if format == nil { + format = scale.FormatTick + } + labels := rowLabels(ticks, a.Min, a.Max, func(v float64) int { return tickRowOf(m.h, a.Min, a.Max, v) }, format) + width := 0 + for _, lbl := range labels { + width = max(width, lipgloss.Width(lbl)) + } + return labels, width +} + +// appendRightAxis adds the right axis line and the label column to every plot line of out. +func (m *Model) appendRightAxis(out string, labels map[int]string, width, zeroRow int) string { + style := m.axisStyle.Width(width).Align(lipgloss.Left) + lines := strings.Split(out, "\n") + for r := range lines { + axis := "│" + if m.zeroBaseline && r == zeroRow { + axis = "┤" + } + lines[r] += m.axisStyle.Render(axis) + style.Render(labels[r]) + } + return strings.Join(lines, "\n") +} + +// withoutRight returns the series data with the right-axis series left out as nil. +func withoutRight(seriesData [][]float64, onRight []bool) [][]float64 { + if !slices.Contains(onRight, true) { + return seriesData + } + out := slices.Clone(seriesData) + for i, right := range onRight { + if right { + out[i] = nil + } + } + return out +} + +// rightChart is the Chart a renderer sees when series are on the right axis: their values are already in the left drawing space. +type rightChart struct { + Chart + data map[string][]float64 +} + +// SeriesData returns the mapped values of a right-axis series. +func (c rightChart) SeriesData(name string) []float64 { + if d, ok := c.data[name]; ok { + return d + } + return c.Chart.SeriesData(name) +} + +// SeriesInverted is false for a right-axis series: its values are negated before the mapping. +func (c rightChart) SeriesInverted(name string) bool { + if _, ok := c.data[name]; ok { + return false + } + return c.Chart.SeriesInverted(name) +} + +// toRightPlot maps the right-axis series into the range chart sees and stores them in plot. Gaps stay gaps. +// It returns the Chart the renderers get. +func (m *Model) toRightPlot(names []string, onRight []bool, seriesData, plot [][]float64, chart Chart) Chart { + lo, hi := chart.Min(), chart.Max() + a := m.rightAxis + byName := map[string][]float64{} + for i, right := range onRight { + if !right || seriesData[i] == nil { + continue + } + d := make([]float64, len(seriesData[i])) + for j, v := range seriesData[i] { + d[j] = v + if !isGap(v) { + d[j] = lo + (v-a.Min)/(a.Max-a.Min)*(hi-lo) + } + } + plot[i], byName[names[i]] = d, d + } + if len(byName) == 0 { + return chart + } + return rightChart{Chart: chart, data: byName} +} diff --git a/rightaxis_test.go b/rightaxis_test.go new file mode 100644 index 0000000..d10a9ef --- /dev/null +++ b/rightaxis_test.go @@ -0,0 +1,399 @@ +package pulse_test + +import ( + "fmt" + "math" + "strings" + "testing" + + "charm.land/lipgloss/v2" + "github.com/charmbracelet/x/ansi" + "github.com/ingvarch/pulse" + "github.com/ingvarch/pulse/scale" +) + +const raW, raH = 20, 11 + +func raOpts(mode pulse.RenderMode, opts ...pulse.Option) []pulse.Option { + return append([]pulse.Option{ + pulse.WithMode(mode), + pulse.WithRange(0, 100), + pulse.WithGrid(false), + pulse.WithFill(false), + pulse.WithLineWidth(1), + }, opts...) +} + +func raUnits(v float64) string { return fmt.Sprintf("%.0fu", v) } + +// raPlotRows returns the plot cells of every View line, with the left label column and the right axis cut off. +func raPlotRows(m *pulse.Model, labelWidth int) []string { + var rows []string + for _, line := range strings.Split(ansi.Strip(m.View()), "\n") { + rows = append(rows, string([]rune(line)[labelWidth+1:labelWidth+1+m.Width()])) + } + return rows[:m.Height()] +} + +func pushConst(m *pulse.Model, name string, v float64) { + for range raW { + m.PushSeries(name, v) + } +} + +func TestRightAxis_SeriesUsesRightRange(t *testing.T) { + forModes(t, func(t *testing.T, mode pulse.RenderMode) { + right := pulse.New(raW, raH, raOpts(mode, + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 250}), + pulse.WithSeriesRightAxis("s"))...) + onLeft := pulse.New(raW, raH, raOpts(mode)...) + same := pulse.New(raW, raH, raOpts(mode)...) + pushConst(right, "s", 50) + pushConst(onLeft, "s", 50) + pushConst(same, "s", 20) + + if got, want := raPlotRows(right, 4), raPlotRows(same, 4); strings.Join(got, "\n") != strings.Join(want, "\n") { + t.Fatalf("right series 50 on 0..250 must draw like 20 on 0..100:\n%s\nwant\n%s", strings.Join(got, "\n"), strings.Join(want, "\n")) + } + if got, left := raPlotRows(right, 4), raPlotRows(onLeft, 4); strings.Join(got, "\n") == strings.Join(left, "\n") { + t.Fatal("the same value must land on different rows on the left and right axis") + } + }) +} + +func TestRightAxis_GapsStayGaps(t *testing.T) { + forModes(t, func(t *testing.T, mode pulse.RenderMode) { + m := pulse.New(raW, raH, raOpts(mode, + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 250}), + pulse.WithSeriesRightAxis("s"))...) + ref := pulse.New(raW, raH, raOpts(mode)...) + for i := range raW { + switch i % 4 { + case 1: + m.PushSeries("s", math.NaN()) + ref.PushSeries("s", math.NaN()) + case 2: + m.PushSeries("s", math.Inf(1)) + ref.PushSeries("s", math.Inf(1)) + case 3: + m.PushSeries("s", math.Inf(-1)) + ref.PushSeries("s", math.Inf(-1)) + default: + m.PushSeries("s", 125) + ref.PushSeries("s", 50) + } + } + got, want := strings.Join(raPlotRows(m, 4), "\n"), strings.Join(raPlotRows(ref, 4), "\n") + if strings.TrimSpace(want) == "" || got != want { + t.Fatalf("gaps must stay gaps and points must be drawn:\n%s\nwant\n%s", got, want) + } + }) +} + +func TestRightAxis_LinesHaveEqualWidth(t *testing.T) { + forModes(t, func(t *testing.T, mode pulse.RenderMode) { + for _, xaxis := range []string{"", "at", "every"} { + opts := raOpts(mode, pulse.WithGrid(true)) + switch xaxis { + case "every": + opts = append(opts, pulse.WithXAxis(func(age int) string { return fmt.Sprint(age) })) + case "at": + opts = append(opts, pulse.WithXAxisAt(func(age int) string { + if age%5 == 0 { + return fmt.Sprint(age) + } + return "" + })) + } + plain := pulse.New(raW, raH, opts...) + m := pulse.New(raW, raH, append(opts, pulse.WithRightAxis(pulse.Axis{ + Min: 0, Max: 5000, Ticks: []float64{0, 2500, 5000}, Formatter: raUnits, + }))...) + for _, c := range []*pulse.Model{plain, m} { + pushConst(c, "s", 50) + } + base := lipgloss.Width(strings.SplitN(plain.View(), "\n", 2)[0]) + lines := strings.Split(m.View(), "\n") + if want := len(strings.Split(plain.View(), "\n")); len(lines) != want { + t.Fatalf("xaxis=%q: %d lines, want %d", xaxis, len(lines), want) + } + for i, line := range lines { + if got, want := lipgloss.Width(line), base+1+len("5000u"); got != want { + t.Fatalf("xaxis=%q: line %d width = %d, want %d: %q", xaxis, i, got, want, ansi.Strip(line)) + } + } + } + }) +} + +func TestRightAxis_LabelsOnTickRowsGridOnLeftTicks(t *testing.T) { + forModes(t, func(t *testing.T, mode pulse.RenderMode) { + m := pulse.New(raW, raH, raOpts(mode, + pulse.WithGrid(true), + pulse.WithTicks(0, 25, 75, 100), + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 5000, Ticks: []float64{0, 2500, 5000, 9000}, Formatter: raUnits}))...) + lines := strings.Split(ansi.Strip(m.View()), "\n") + wantLabel := map[int]string{0: "5000u", 5: "2500u", 10: "0u"} + for r, line := range lines { + right := strings.TrimSpace(string([]rune(line)[4+1+raW+1:])) + if right != wantLabel[r] { + t.Fatalf("row %d: right label = %q, want %q", r, right, wantLabel[r]) + } + } + if strings.Contains(lines[5], "─") { + t.Fatalf("row 5 is a right tick only and must have no grid line: %q", lines[5]) + } + for _, r := range []int{0, 3, 8, 10} { + if !strings.Contains(lines[r], "─") { + t.Fatalf("row %d is a left tick and must have a grid line: %q", r, lines[r]) + } + } + }) +} + +func TestRightAxis_DefaultTicksAndFormatter(t *testing.T) { + m := pulse.New(raW, raH, pulse.WithRange(0, 100), pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 6000})) + lines := strings.Split(ansi.Strip(m.View()), "\n") + if got, want := strings.TrimSpace(string([]rune(lines[0])[4+1+raW+1:])), scale.FormatTick(6000); got != want { + t.Fatalf("top right label = %q, want %q", got, want) + } +} + +func TestRightAxis_AxisLineMirrorsZeroBaseline(t *testing.T) { + m := pulse.New(raW, raH, pulse.WithRange(-100, 100), pulse.WithZeroBaseline(true), + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 10})) + for r, line := range strings.Split(ansi.Strip(m.View()), "\n") { + want := '│' + if r == 5 { + want = '┤' + } + if got := []rune(line)[4+1+raW]; got != want { + t.Fatalf("row %d: right axis glyph = %q, want %q", r, got, want) + } + } +} + +func TestRightAxis_XAxisRowClosesWithCorner(t *testing.T) { + m := pulse.New(raW, raH, pulse.WithXAxis(func(age int) string { return fmt.Sprint(age) }), + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 5000, Ticks: []float64{5000}, Formatter: raUnits})) + lines := strings.Split(ansi.Strip(m.View()), "\n") + axis := []rune(lines[raH]) + if got := axis[4+1+raW]; got != '┘' { + t.Fatalf("X axis row corner = %q, want ┘: %q", got, lines[raH]) + } + if rest := strings.TrimSpace(string(axis[4+1+raW+1:])); rest != "" { + t.Fatalf("X axis row must be blank after the corner, got %q", rest) + } +} + +func TestRightAxis_HigherTickWinsOnSharedRow(t *testing.T) { + m := pulse.New(raW, raH, pulse.WithRange(0, 100), + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 5000, Ticks: []float64{100, 5000, 0}, Formatter: raUnits})) + lines := strings.Split(ansi.Strip(m.View()), "\n") + if got := strings.TrimSpace(string([]rune(lines[raH-1])[4+1+raW+1:])); got != "100u" { + t.Fatalf("bottom right label = %q, want the higher tick 100u", got) + } +} + +func TestRightAxis_LegendMarksRightSeries(t *testing.T) { + m := pulse.New(raW, raH, pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 5000})) + m.PushSeries("cpu", 10) + m.PushSeries("rps", 20) + m.SetSeriesRightAxis("rps", true) + if got := ansi.Strip(m.Legend()); got != "■ cpu ■ rps →" { + t.Fatalf("Legend() = %q", got) + } + box := ansi.Strip(m.LegendBox()) + if !strings.Contains(box, "rps → ") || strings.Contains(box, "cpu →") { + t.Fatalf("LegendBox() must mark rps only:\n%s", box) + } +} + +func TestRightAxis_AutoRangeIgnoresRightSeries(t *testing.T) { + forModes(t, func(t *testing.T, mode pulse.RenderMode) { + m := pulse.New(raW, raH, pulse.WithMode(mode), pulse.WithAutoRange(true), + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 5000}), + pulse.WithSeriesRightAxis("rps")) + m.PushSeries("cpu", 10) + m.PushSeries("rps", 4000) + m.View() + if m.Max() > 20 { + t.Fatalf("Max = %v, want a range that fits the left series only", m.Max()) + } + }) +} + +func TestRightAxis_ThresholdColorsSkipRightSeries(t *testing.T) { + forModes(t, func(t *testing.T, mode pulse.RenderMode) { + build := func(right bool) *pulse.Model { + opts := raOpts(mode, + pulse.WithSeriesStyle("s", thBase), + pulse.WithThresholds(pulse.Threshold{Value: 10, Style: thCrit}), + pulse.WithThresholdColors(true), + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 100})) + if right { + opts = append(opts, pulse.WithSeriesRightAxis("s")) + } + m := pulse.New(raW, raH, opts...) + pushConst(m, "s", 50) + return m + } + row := rowOfGlyph(t, build(true), 10) + if c := cellAt(build(false), row, 10); !styledAs(c, thCrit) { + t.Fatalf("control: left series cell = %q, want the threshold style", c) + } + if c := cellAt(build(true), row, 10); !styledAs(c, thBase) { + t.Fatalf("right series cell = %q, want the series style", c) + } + }) +} + +func TestRightAxis_InvertedSeriesIsNegatedBeforeMapping(t *testing.T) { + m := pulse.New(raW, raH, pulse.WithRange(-100, 100), pulse.WithGrid(false), pulse.WithFill(false), + pulse.WithRightAxis(pulse.Axis{Min: -250, Max: 250}), + pulse.WithSeriesRightAxis("s"), pulse.WithSeriesInverted("s", true)) + ref := pulse.New(raW, raH, pulse.WithRange(-100, 100), pulse.WithGrid(false), pulse.WithFill(false)) + pushConst(m, "s", 125) + pushConst(ref, "s", -50) + if got, want := raPlotRows(m, 4), raPlotRows(ref, 4); strings.Join(got, "\n") != strings.Join(want, "\n") { + t.Fatalf("inverted right series 125 must draw like -50:\n%s\nwant\n%s", strings.Join(got, "\n"), strings.Join(want, "\n")) + } +} + +func TestRightAxis_LogScaleKeepsRightAxisLinear(t *testing.T) { + m := pulse.New(raW, raH, pulse.WithRange(1, 100), pulse.WithLogScale(true), pulse.WithGrid(false), pulse.WithFill(false), + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 250}), pulse.WithSeriesRightAxis("s")) + ref := pulse.New(raW, raH, pulse.WithRange(1, 100), pulse.WithLogScale(true), pulse.WithGrid(false), pulse.WithFill(false)) + pushConst(m, "s", 125) + pushConst(ref, "s", 10) // the middle of a 1..100 log range + if got, want := raPlotRows(m, 4), raPlotRows(ref, 4); strings.Join(got, "\n") != strings.Join(want, "\n") { + t.Fatalf("right series at the middle of 0..250 must sit at the middle row:\n%s\nwant\n%s", strings.Join(got, "\n"), strings.Join(want, "\n")) + } +} + +func TestSetRightAxis_Validation(t *testing.T) { + m := pulse.New(raW, raH) + m.SetRightAxis(pulse.Axis{Min: math.NaN(), Max: 10}) + if _, ok := m.RightAxis(); ok { + t.Fatal("a NaN bound must not create the axis") + } + m.SetRightAxis(pulse.Axis{Min: 0, Max: math.Inf(1)}) + if _, ok := m.RightAxis(); ok { + t.Fatal("an infinite bound must not create the axis") + } + m.SetRightAxis(pulse.Axis{Min: 20, Max: 10}) + if a, ok := m.RightAxis(); !ok || a.Min != 10 || a.Max != 20 { + t.Fatalf("RightAxis() = %+v, %v, want swapped 10..20", a, ok) + } + m.SetRightAxis(pulse.Axis{Min: math.NaN(), Max: 99}) + if a, _ := m.RightAxis(); a.Min != 10 || a.Max != 20 { + t.Fatalf("a NaN bound must keep the range, got %+v", a) + } + m.SetRightAxis(pulse.Axis{Min: 5, Max: 5}) + if a, _ := m.RightAxis(); a.Min != 5 || a.Max != 6 { + t.Fatalf("equal bounds must widen to 5..6, got %+v", a) + } +} + +func TestSetSeriesRightAxis_Toggle(t *testing.T) { + m := pulse.New(raW, raH) + if m.SeriesRightAxis("s") { + t.Fatal("a new series is on the left axis") + } + m.SetSeriesRightAxis("s", true) + if !m.SeriesRightAxis("s") { + t.Fatal("SeriesRightAxis must report true") + } + m.SetSeriesRightAxis("s", false) + if m.SeriesRightAxis("s") { + t.Fatal("SeriesRightAxis must report false") + } +} + +func TestRightAxis_SeriesWithoutAxisStaysOnLeft(t *testing.T) { + m := pulse.New(raW, raH, raOpts(pulse.ModeLines, pulse.WithSeriesRightAxis("s"))...) + ref := pulse.New(raW, raH, raOpts(pulse.ModeLines)...) + pushConst(m, "s", 50) + pushConst(ref, "s", 50) + if m.View() != ref.View() { + t.Fatal("without a right axis the flag must change nothing") + } + if strings.Contains(m.Legend(), "→") { + t.Fatal("without a right axis the legend must not mark the series") + } +} + +func TestRightAxis_NegativeStyleSkipsRightSeries(t *testing.T) { + forModes(t, func(t *testing.T, mode pulse.RenderMode) { + build := func(right bool, v float64) *pulse.Model { + opts := []pulse.Option{ + pulse.WithMode(mode), pulse.WithRange(-100, 100), pulse.WithZeroBaseline(true), + pulse.WithGrid(false), pulse.WithFill(false), pulse.WithLineWidth(1), + pulse.WithSeriesStyle("s", thBase), pulse.WithSeriesNegativeStyle("s", thNeg), + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 100}), + } + if right { + opts = append(opts, pulse.WithSeriesRightAxis("s")) + } + m := pulse.New(raW, raH, opts...) + pushConst(m, "s", v) + return m + } + left := build(false, -80) + if c := cellAt(left, 9, 10); !styledAs(c, thNeg) { + t.Fatalf("control: left cell = %q, want the negative style", c) + } + right := build(true, 10) // maps to -80 on the left range + if c := cellAt(right, 9, 10); !styledAs(c, thBase) { + t.Fatalf("right cell = %q, want the series style", c) + } + }) +} + +type raRecorder struct { + data []float64 + inverted bool + right []bool + min, max float64 +} + +func (r *raRecorder) Render(c pulse.Chart, ctx pulse.RenderContext) string { + r.data, r.inverted, r.right = c.SeriesData("r"), c.SeriesInverted("r"), ctx.RightAxis + r.min, r.max = c.Min(), c.Max() + return "" +} + +func TestRightAxis_CustomRendererGetsMappedValues(t *testing.T) { + rec := &raRecorder{} + m := pulse.New(4, raH, pulse.WithRenderer(rec), pulse.WithRange(0, 100), + pulse.WithRightAxis(pulse.Axis{Min: 0, Max: 250}), + pulse.WithSeriesRightAxis("r"), pulse.WithSeriesInverted("r", true)) + m.PushSeries("r", 125) + m.PushSeries("r", math.NaN()) + _ = m.View() + if len(rec.data) != 2 || rec.data[0] != -50 { + t.Fatalf("Chart.SeriesData = %v, want the negated 125 mapped to -50", rec.data) + } + if !math.IsNaN(rec.data[1]) { + t.Fatalf("a gap must stay a gap, got %v", rec.data[1]) + } + if rec.inverted { + t.Fatal("SeriesInverted must be false for a right-axis series") + } + if len(rec.right) != 2 || rec.right[0] || !rec.right[1] { + t.Fatalf("ctx.RightAxis = %v, want [false true]", rec.right) + } +} + +func TestRightAxis_InvertedSeriesOnLogScale(t *testing.T) { + m := pulse.New(raW, raH, pulse.WithRange(1, 100), pulse.WithLogScale(true), pulse.WithGrid(false), pulse.WithFill(false), + pulse.WithRightAxis(pulse.Axis{Min: -250, Max: 250}), + pulse.WithSeriesRightAxis("s"), pulse.WithSeriesInverted("s", true)) + ref := pulse.New(raW, raH, pulse.WithRange(1, 100), pulse.WithLogScale(true), pulse.WithGrid(false), pulse.WithFill(false), + pulse.WithRightAxis(pulse.Axis{Min: -250, Max: 250}), pulse.WithSeriesRightAxis("s")) + pushConst(m, "s", -125) + pushConst(ref, "s", 125) + if got, want := raPlotRows(m, 4), raPlotRows(ref, 4); strings.Join(got, "\n") != strings.Join(want, "\n") || strings.TrimSpace(strings.Join(want, "")) == "" { + t.Fatalf("inverted right series -125 must draw like 125:\n%s\nwant\n%s", strings.Join(got, "\n"), strings.Join(want, "\n")) + } +} diff --git a/view.go b/view.go index 905703f..98b5a84 100644 --- a/view.go +++ b/view.go @@ -3,6 +3,7 @@ package pulse import ( "math" "slices" + "strings" "charm.land/lipgloss/v2" @@ -49,32 +50,52 @@ func (m *Model) formatValue(v float64) string { return scale.FormatTick(v) } +// rowLabels labels the ticks inside lo..hi by their row. When two ticks share a row, the later one in ticks wins. +func rowLabels(ticks []float64, lo, hi float64, rowOf func(float64) int, format func(float64) string) map[int]string { + labels := map[int]string{} + for i := len(ticks) - 1; i >= 0; i-- { + t := ticks[i] + if isGap(t) || t < lo || t > hi { + continue + } + row := rowOf(t) + if _, taken := labels[row]; !taken { + labels[row] = format(t) + } + } + return labels +} + // quarterCols returns the columns at 1/4, 2/4 and 3/4 of width w. func quarterCols(w int) [3]int { return [3]int{w / 4, 2 * w / 4, 3 * w / 4} } // View renders the chart: Y-axis labels + axis border + plot area, +// then the right axis line and its label column when WithRightAxis is set, // then the X axis and label rows when WithXAxis is set. func (m *Model) View() string { names := make([]string, 0, len(m.order)+1) names = append(names, "") names = append(names, m.order...) seriesData := make([][]float64, len(names)) + onRight := make([]bool, len(names)) for idx, name := range names { + onRight[idx] = m.onRight(name) if s, ok := m.series[name]; ok { data := m.window(s) - if !m.logScale && (s.inverted || (name == "" && m.inverted)) { + if (!m.logScale || onRight[idx]) && (s.inverted || (name == "" && m.inverted)) { data = negated(data) } seriesData[idx] = data } } + leftData := withoutRight(seriesData, onRight) if m.autoRange { if m.logScale { - m.min, m.max = fitLogRange(seriesData) + m.min, m.max = fitLogRange(leftData) } else { - m.min, m.max = m.fitRange(seriesData) + m.min, m.max = m.fitRange(leftData) } m.rangeMin, m.rangeMax = m.min, m.max } @@ -95,17 +116,7 @@ func (m *Model) View() string { } } - rowLabel := map[int]string{} - for i := len(ticks) - 1; i >= 0; i-- { - t := ticks[i] - if isGap(t) || t < lo || t > hi { - continue - } - row := m.tickRow(t) - if _, taken := rowLabel[row]; !taken { - rowLabel[row] = m.formatValue(t) - } - } + rowLabel := rowLabels(ticks, lo, hi, m.tickRow, m.formatValue) labelWidth := m.labelWidth if labelWidth <= 0 { @@ -140,9 +151,11 @@ func (m *Model) View() string { } var chart Chart = m + plot := leftData if m.logScale { - seriesData, chart = m.toLogPlot(names, seriesData) + plot, chart = m.toLogPlot(names, leftData) } + chart = m.toRightPlot(names, onRight, seriesData, plot, chart) styles := make([]lipgloss.Style, len(names)) negativeStyles := make([]lipgloss.Style, len(names)) @@ -164,7 +177,8 @@ func (m *Model) View() string { Styles: styles, NegativeStyles: negativeStyles, HasNegStyles: hasNegStyles, - SeriesData: seriesData, + SeriesData: plot, + RightAxis: onRight, GridStyle: gridStyle, ZeroRow: zeroRow, } @@ -176,11 +190,18 @@ func (m *Model) View() string { r = LinesRenderer{} } out := r.Render(chart, ctx) + corner, pad := "", "" + if m.hasRightAxis { + labels, width := m.rightRowLabels() + out = m.appendRightAxis(out, labels, width, zeroRow) + corner = m.axisStyle.Render("┘") + strings.Repeat(" ", width) + pad = strings.Repeat(" ", 1+width) + } if m.xAxisLabel == nil { return out } axis, labels := m.xAxisRows(labelWidth, placed) - return out + "\n" + axis + "\n" + labels + return out + "\n" + axis + corner + "\n" + labels + pad } // String implements fmt.Stringer, returning the rendered chart View().