From c69b89cc42747d5bb3e7dd7c0088a229820e75ae Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 9 Nov 2023 02:01:00 +0100 Subject: [PATCH] Update example: shapes_splines_drawing --- examples/shapes/shapes_splines_drawing.c | 107 ++++++++++++++------- examples/shapes/shapes_splines_drawing.png | Bin 0 -> 26123 bytes 2 files changed, 73 insertions(+), 34 deletions(-) create mode 100644 examples/shapes/shapes_splines_drawing.png diff --git a/examples/shapes/shapes_splines_drawing.c b/examples/shapes/shapes_splines_drawing.c index 17dd00752..435ff79b9 100644 --- a/examples/shapes/shapes_splines_drawing.c +++ b/examples/shapes/shapes_splines_drawing.c @@ -13,6 +13,9 @@ #include "raylib.h" +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" // Required for UI controls + #include // Required for: NULL #define MAX_SPLINE_POINTS 32 @@ -46,11 +49,11 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - splines drawing"); Vector2 points[MAX_SPLINE_POINTS] = { - { 100.0f, 200.0f }, - { 300.0f, 400.0f }, - { 500.0f, 300.0f }, - { 700.0f, 100.0f }, - { 200.0f, 100.0f }, + { 50.0f, 400.0f }, + { 160.0f, 220.0f }, + { 340.0f, 380.0f }, + { 520.0f, 60.0f }, + { 710.0f, 260.0f }, }; int pointCount = 5; @@ -59,15 +62,19 @@ int main(void) Vector2 *selectedControlPoint = NULL; Vector2 *focusedControlPoint = NULL; - int splineType = SPLINE_LINEAR; // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier - // Cubic Bezier control points initialization ControlPoint control[MAX_SPLINE_POINTS] = { 0 }; for (int i = 0; i < pointCount - 1; i++) { - control[i].start = (Vector2){ points[i].x - 20, points[i].y - 20 }; - control[i].end = (Vector2){ points[i + 1].x + 20, points[i + 1].y + 20 }; + control[i].start = (Vector2){ points[i].x + 50, points[i].y }; + control[i].end = (Vector2){ points[i + 1].x - 50, points[i + 1].y }; } + + // Spline config variables + float splineThickness = 8.0f; + int splineTypeActive = SPLINE_LINEAR; // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier + bool splineTypeEditMode = false; + bool splineHelpersActive = true; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -104,7 +111,7 @@ int main(void) } // Cubic Bezier spline control points logic - if ((splineType == SPLINE_BEZIER) && (focusedPoint == -1)) + if ((splineTypeActive == SPLINE_BEZIER) && (focusedPoint == -1)) { // Spline control point focus and selection logic for (int i = 0; i < pointCount; i++) @@ -133,10 +140,10 @@ int main(void) } // Spline selection logic - if (IsKeyPressed(KEY_ONE)) splineType = 0; - else if (IsKeyPressed(KEY_TWO)) splineType = 1; - else if (IsKeyPressed(KEY_THREE)) splineType = 2; - else if (IsKeyPressed(KEY_FOUR)) splineType = 3; + if (IsKeyPressed(KEY_ONE)) splineTypeActive = 0; + else if (IsKeyPressed(KEY_TWO)) splineTypeActive = 1; + else if (IsKeyPressed(KEY_THREE)) splineTypeActive = 2; + else if (IsKeyPressed(KEY_FOUR)) splineTypeActive = 3; //---------------------------------------------------------------------------------- // Draw @@ -145,29 +152,44 @@ int main(void) ClearBackground(RAYWHITE); - if (splineType == SPLINE_LINEAR) + if (splineTypeActive == SPLINE_LINEAR) { // Draw spline: linear - DrawSplineLinear(points, pointCount, 2.0f, RED); + DrawSplineLinear(points, pointCount, splineThickness, RED); } - else if (splineType == SPLINE_BASIS) + else if (splineTypeActive == SPLINE_BASIS) { // Draw spline: basis - DrawSplineBasis(points, pointCount, 2.0f, RED); - //for (int i = 0; i < (pointCount - 3); i++) DrawSplineBasisSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, BLUE); + DrawSplineBasis(points, pointCount, splineThickness, RED); // Provide connected points array + + /* + for (int i = 0; i < (pointCount - 3); i++) + { + // Drawing individual segments, not considering thickness connection compensation + DrawSplineSegmentBasis(points[i], points[i + 1], points[i + 2], points[i + 3], splineThickness, MAROON); + } + */ } - else if (splineType == SPLINE_CATMULLROM) + else if (splineTypeActive == SPLINE_CATMULLROM) { // Draw spline: catmull-rom - DrawSplineCatmullRom(points, pointCount, 2.0f, RED); - //for (int i = 0; i < (pointCount - 3); i++) DrawSplineCatmullRomSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, Fade(BLUE, 0.4f)); + DrawSplineCatmullRom(points, pointCount, splineThickness, RED); // Provide connected points array + + /* + for (int i = 0; i < (pointCount - 3); i++) + { + // Drawing individual segments, not considering thickness connection compensation + DrawSplineSegmentCatmullRom(points[i], points[i + 1], points[i + 2], points[i + 3], splineThickness, MAROON); + } + */ } - else if (splineType == SPLINE_BEZIER) + else if (splineTypeActive == SPLINE_BEZIER) { // Draw spline: cubic-bezier (with control points) for (int i = 0; i < pointCount - 1; i++) { - DrawSplineSegmentBezierCubic(points[i], control[i].start, control[i].end, points[i + 1], 10.0f, RED); + // Drawing individual segments, not considering thickness connection compensation + DrawSplineSegmentBezierCubic(points[i], control[i].start, control[i].end, points[i + 1], splineThickness, RED); // Every cubic bezier point should have two control points DrawCircleV(control[i].start, 4, GOLD); @@ -178,22 +200,39 @@ int main(void) DrawLineEx(points[i + 1], control[i].end, 1.0, LIGHTGRAY); // Draw spline control lines - DrawLineV(points[i], control[i].start, LIGHTGRAY); - DrawLineV(control[i].start, control[i].end, LIGHTGRAY); - DrawLineV(control[i].end, points[i + 1], LIGHTGRAY); + DrawLineV(points[i], control[i].start, GRAY); + //DrawLineV(control[i].start, control[i].end, LIGHTGRAY); + DrawLineV(control[i].end, points[i + 1], GRAY); } } - // Draw spline key-points - for (int i = 0; i < pointCount; i++) + if (splineHelpersActive) { - DrawCircleV(points[i], (focusedPoint == i)? 8.0f : 5.0f, (focusedPoint == i)? BLUE: RED); - if ((splineType != SPLINE_LINEAR) && - (splineType != SPLINE_BEZIER) && - (i < pointCount - 1)) DrawLineV(points[i], points[i + 1], LIGHTGRAY); + // Draw spline point helpers + for (int i = 0; i < pointCount; i++) + { + DrawCircleLinesV(points[i], (focusedPoint == i)? 12.0f : 8.0f, (focusedPoint == i)? BLUE: DARKBLUE); + if ((splineTypeActive != SPLINE_LINEAR) && + (splineTypeActive != SPLINE_BEZIER) && + (i < pointCount - 1)) DrawLineV(points[i], points[i + 1], GRAY); + + DrawText(TextFormat("[%.0f, %.0f]", points[i].x, points[i].y), points[i].x, points[i].y + 10, 10, BLACK); + } } + + // Check all possible UI states that require controls lock + if (splineTypeEditMode) GuiLock(); - // TODO: Draw help + // Draw spline config + GuiLabel((Rectangle){ 12, 62, 140, 24 }, TextFormat("Spline thickness: %i", (int)splineThickness)); + GuiSliderBar((Rectangle){ 12, 60 + 24, 140, 16 }, NULL, NULL, &splineThickness, 1.0f, 40.0f); + + GuiCheckBox((Rectangle){ 12, 110, 20, 20 }, "Show point helpers", &splineHelpersActive); + + GuiUnlock(); + + GuiLabel((Rectangle){ 12, 10, 140, 24 }, "Spline type:"); + if (GuiDropdownBox((Rectangle){ 12, 8 + 24, 140, 28 }, "LINEAR;BSPLINE;CATMULLROM;BEZIER", &splineTypeActive, splineTypeEditMode)) splineTypeEditMode = !splineTypeEditMode; EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/shapes/shapes_splines_drawing.png b/examples/shapes/shapes_splines_drawing.png new file mode 100644 index 0000000000000000000000000000000000000000..686f04c11831316662f22dd8d9ebd7d180eee5fd GIT binary patch literal 26123 zcmeFZdpMNa8$UcJV`MN=8pAM?3Pm{#ITVB4jEE++OFE51nh-Y ziq0oeXrl}gMI{tb2^C4b_cK%5^xb>E?;r1V{r>v>GncELXFY4(>pp(&&suvqE^LKK zlu0NQN@20XZ>}g5mWD#1?-20tFVnovRHINE8y5d&yJGu#udiP|_U%qsW=O#Z>HqN) zNO8^hz@^ho?~#C8%l?^5IW zx>Uh7bFRs>D@~KV@U`uR7q9rNs=nwK8orUSChcF8FgXGB(VpM0NVw&K2~Na54mQYM z6?AH5Gtt#&xx;zC&@3Ou^?%nM$A!lg=!yj1)tmy?%vjP##8xlXuuI|IkE=l=62 zm{3ey`LHnrk6D%I8>;GE8uTBQ^CkiH`_l3l3x&tY7`?IC^>t;B)R-$}m&ZPg@-I)U zJ1crn#apTV?^gFF5!Lzl+=lPV8(YdR?NHyL@`ZQOlOg}_ODHQ#K-B~#@kQer!qvY2 zoH4Qrd?10d3-8Ui!2Y*2N&XGBieQ+6ulr9+<`L@W;8<2P;pQ#>{t=cv-=h?3oJ06m z!66?>SVu^H0Ji?-|Hal(_fK*^e*BoiddMS?sF+2I776oA|6R2PNVGok`uX!Oi)b1O z=oosj-{Z3)4&~ovnn@zBi!sfi{paL*R53zoy6EnI>+%1Wr-~LSR`V$Tt`CTdjiMV` zf_(n`&rSG0F@-YY{n}yQzK*)xJeHkZ%~Y^(tW~S?|Bn!%Vb9-qf<3430AB-NO;rlD z$hP!s~rK?zF`Lc5EmpgFm4-xsuvVNBI^iRpHHVscCd^)Za!XzdA0h9)c*s_Q(g(+ZJ#O zVlAf^Kd9#EcrzX;JR*@lP)WnfqPi*>))sDYDvhJGd#UAF>4Y#lmX($;{<*Xw*Pk4U z_IG-kwuAc)dHtipJwzL1+TKTNY^|nFvP<#v++;m)2zx-iwnX=eC0&@CCbA2ZR`WtF zOtsV5fkTRK;#$sAq7QnfR95f^#C#HYVZy=MmgC`x(JkFB*gvk+Td5;&#{@{f%~Qb? z&s=%&`-*Z^Mr0JafEu-{yqMIqS}i3J*6K?)yoQsnTI?p+bN7;*`c)x+%jT&|h*x7{ zYBYxLVoY`xo4u30KE_HqCM_7UA|2Jo*N+-KY8!1E=Yb|!JyYH|a&8x;VyEFnp^*&* zW3PuJ-;Nspa%_t1=Z_zau3WvEQaK1#MT}l|J=|!@)Tt>6P0~^VH|kDU5cQQj7vp8K zV-Tm0?|go6yTU3V?JGa;*;k|Hps%yHhcY5Te;wk>di30ca^cde6q{Bd8Pk(1?^cEJ zmRaJma;0j3Fgcl+y=C)exq{a_H1C*?PrC4RUn06x<8<_@7sB0D-$2SvAItaXm8J$ zs>$s{huG#Sk+4mpD3QR*mZi-DO>-xRQ-uCs7CS&fg)MM@{M?5h#{3iWJNetuo7MHB zdk*Jq8K>e&&)+=t!mn|r-=N>~^fx?VKe_oTbHikr!6|1C$-7!jyNe?gE?;&p<1Wg| zciO?DB6&G>(%C%Fe()frR?=3WZH{w)+>YO7+`M3Id9mt~@)wGO-Xkqp57Lzs4nBLl zl4yOrnRw5s*>K8j3zlK2Tk=P>?7UoM(oTWZtdUP^_zUbGK9D)TSRwqj>{%B=;n4Zm zZGZ8eQ5B-;YT~cO{ra^TxNk>pqCWLLTrFQ7`r1Aq!K*Ep_>iBglwNkMq-DNEtR%YR3yD2~dKwaa>p5lyyY@n1w8Qjb7sBA%*Tt*T zMER?P_nJA1VjuSfK0l#~9u$l~QMRZA-A5|B5s~bamAC3yN=NI*0OIos%h^4X{i3N; z_3B*lPA`J>>-X!s#wx6-id$%)=}(l+WSJdksB05+FRMO$GtrZg_AJ!eeSt!iI3+CT zoA?I&l|UcBn%AKFBDq7)wsK`pjF|RTU5?4}{fIf}wc2)*bG5>?*PeGsXVfnlE|{j` zxjpLhEUf=2)w45~30mdd5&<&rU)tqPk;`Ng4_?#;2tKT^~n5X@+ z4w}0z;ViZ@O|!n?J5>L)=gJp)>_6v1BEGdLdmvY+g3Bo{zxNz?EA-2A_@~n(mn~R7~;)@F_Nw z&A$k5g&nGSl)I}vp`%M{&?;ex4sJ)R>80YTnQ4)y%`Vlw?8%+vUE)!5G;6$(i#ku8 zxnAg|;65q-&u`QtNjJPFnZ?@GX51)sSS47UAxf?09fF8&KVO_}oRUaVEKY}bRA1jp zyxZ%}c?S)GW_201jnlp@ji-8#%5F$R9n%O-M74X`Unsdx;(di+|^o7%XO0|vM2O!UWp(HNJR#;BBsS|dnQn}pDJlTZ9 ziG7RRHZ;ns4Yb$TF7Ar+#Q@-{{Pwam|(+ zvc8!mS?I373QXkimYMg+PLZ`Sl6{{YxxSDLz5jAF3~*Xyx5Ry zB73kkuKz^V;linBiU(v4CBC~<{;FA2yWsC-Gj|Z`FASmIY!7{_?9wVE%dPQ8Cb%B^QvNub0`fcyUuqo7EE$*+BEBI(f&Tc!wq%_+WK&Ka6~tdSM!F4I4Lu{ipe* zmbfrh@u81P8a4cMurpGfQnROE<@-c6@@%JU5Mwq;@Wr{Og;Lj(vOAr!R>42=WlIdf zN9zK$CxCxw@xIosYsv?|f7q*Z@qvivGhtbR079hm$!`fE^~thdz9~AcRp?8m9gd!x z(6*PEr5!#?ra*R=8KJ3_-|FS&IqdCWE5mje*A8*f4JckpXkqf>w_ySd3fK@ z*e|?O#uUu3JUXkK(sFZ3zY+cMQbQ}zCT7qfc5O2Ltc${uBW~AOCif;3Y+w@FH_-Cr zMr=+?{>scs(%Fq`if>W`*B6n`Hu|uX{(p{+LKjDbD_WUQgX&J?Fga*02o49VqNj@3#pU9~hs6jDj zO=o`G$z-vI*$3WDOovB!p@{Z<7VJ_pt|{o|l$y)Px32BtO#k?s0Y}G$d}V?Bj#$f! z4edR_MIsH*J^QW9rwnpJZPT!ATC)|K}csF zCUGeWEkEsClCPkjB%(~0-kARR(?AF1pHg^Qr&Z=SC|jkJjVvd+Qeine}KHhC`KPYeq2=lv(8nfVziu(=zjXndmVd&cH-WvVNti6Q057& zM-$^st>}C8%8c>}Bw~mQfBVFg;`sA}$L;wP^=!74R+kRFehGoFyl>bj-HB^L7v!%O zw(dlFGe}(d72V%uU}*UOM9~Y0hA(?X;_k24Bqp>=wX&H+kMhj5siiYdKHISdA2Kw~Ns3c5N&Nw+GE?P-EgL$(xoJB%9-%65sc}WXbmk@n>(>%_U zD8Vi*q0>41(#g0mJEn3@igAQ-Gw%a!+THRs5wriZO4X= zgd`YlC>Je1hYy^5e5DR9+*q~xhG@EYo~X$`GgI;}AEsbV#G_0npS-+hP4fkrA8ugJ)6*~r7GrV$15xsONaWmc z^3}cZ?%U#$-^r>O1hFE-Uh0)p#U>)rjKS5}untTz{T!RT-qk$kD9cx(Am5}VPEx~U zK8Q-ztgswDK)JZIl2R85YnUuCsdvAa)E<(cvcHPQR7xWWn(w;_$81?4cO4}9`9w?F znk?hWditvd9DR^yl2TwYHaLe{ayQSNOZ&59-HH_q%5G^v85Y{}8`eBzzaJ;xlKU#$ zt8(wCmr?U3$cAj(CxMsPBZpQEj&wZZg1u;EBJBMIk-&S2_waeB+%>)226?=P(|K0o zecq_4s~tanZJO$|Y0IauZr;2pJS7zr!B~DHa4w7?9LEob}4$cE&g)@KIu6-DgLLkSq!oB!wX)7&P-fkm<=JnH-OO$&aOBSRl^N^ z!F?{Uz7`$D@cN0Y4}bKZXLxQ9^pkz z2tlv_$C`gEt!)L)ecwl&+zUG&Fmuyw%2I24PPqy5MM1=9+g3>PirMcuWz%I2nmcH& zs=`{>-8Y(c;uv|?mC1@%!zcUA3pRiGhVU^}GjW0Yg&*)ZibddbzAP7UsaNUDGr}FV zpOX;V&&h-(x5SqT`uHR(d!r+b(`Sw?ba<4sMb(FK?B~)UW&#Raq9xSZLvcFK%rn0> z#gAbCcm!81rMY|>CbQNt{rI$XZ+lpuMQuNuYUzLhMr>ouUmWP=;cX>+-@UH}%~AT^L{g!(Y_T z!s$};H9<|C4_$$Ba!OJ|z1mGd5M!6ZK}qoI{;7|MGvFtb^?<3IV4eoQVbCj3G|u0d z$?}4>3*KJhh&rSlkzsOxwU#rkR0&;AR7*NQ%QwIs-eJsZG8j;7q{#-Ef z9%=@TsUF9!@PvFx4(;2v-#TJ`HCWdhI`m+0p%IUGP15Tv3z`%y%IFpiFG&OO+{4tx za4tOO4F>?2XW3-W@ABHt7DD+a&Je^u32O-a=10pL&>c1`+uJoq*xz(LEo#NI+2O?stpj^7f?dg@*a)wnb} zzPow@`~6up=iBy1lIMPLaMi?U5j?p#5o%D@7q#~5j2t{0>CyZW7ZThE$!0HD2l|=u zsEhCLYPm9M&K33s;%v833KiW0JJY>NxOca}o{KD=Vg4_!-e0^1=dZDJFyIqu1r-^R z#tB0zmz@qSd*d8IMxW>elqO9CbW1xeGh61(lC-(^D9&m2GsR2UECq?gg)`^we;Zg& zB?|9@q8F-Rnyxf@h1xXoax|blT<}ZuBHyJm7kj0&ws08%YNRZ)U-e z$U##94Ie;y>bs=dd8iDSBo9g6HYG z7`ckCCC2rB!nl+HaH=`SQW@ibQhV)SH}(a=ydbA|B3*@{r%Q%1&f5kX3W^wlVAqVF zPEG8N>=)p4S4YXXPd3RxLALu0;)woxUXFUIq>*)2N-{{q{OUX0(=J+K%%P)lWb!IO zh-8pR`4ma1WB#{~gKyyM_&bn?8OD5&P3vW3o(BfFFkzTygp?eWC34AA>Lx+NXJ-tt z)4Hk7-~Tz+P_P8h-TPY#!Fb@)j0NDTLZRm=<=BX0q>-JUFs)G<>!M6^Z1GNnWX&3@ z9Mwel1K|)1PAEjNQnbycdHdg1i5kzbOsctZ(W2Gh4a^bT7%Lr{4Phz!;lqgTd?-hv z2UuUQ`42Uel6W(nd4KS<=mhl0V!`x~WgPhDGGZYtf}frx>QKdG&Xvb?@3ooIpG0w9 z28F?9Nx7LfvDF?>5g}Xs8QI|)Idr$vx&!Zp2zj0=!e?Qe3sccE0qZS>!YwFNcL-wjfdaw9XFtwF< zVFzr1{cP}lwk(x?j-=qrk#ZQ}5}jL?Rtm{9xXK}Dq#?<6uav)XgOU)Ii(5Du3LAN2 z5kwEy09uX&YD!IQn6+?+G#dd)F5VhP^%dATfy~2p!U|L7uhnAjhNfOK6}ItVV!N>h;AE+Uqx= z5ZR%KM^|!G_Y*FLCLDeZq-DW&j-OVf6aaQo2CxA5n_;PrvA*4l_pS2YV_t}dn?(jE zb8VV8W1Uj7L^}n^Zwe+;f2tZ{?fQu!pk|OWye|hO z0zkeucw(+U{z^bBEGBu@+kfaSngRy>_OZiRh7r@7d78&(NDO+@#QLhipkageLVBeO1rQ9pg29p4^UjIY{QL_8RKS}D*|~N#RjZG9%CGic%>c65OlzFO9AfrDGqNt?(nA}(OqaMl}fazY0)`} z*bPuOdv4Ex&I^>68Azq6}63#mzscj3$dhxmf8P^lG2$0F$X@I zIZ-Q3D-I)^hbT5)oBOiHIrdJv`Db@`e=;6VZV1PFX!4QHS*(BrV@v6E;r#0%Q~L zKn{?qev_n7e+qw>SYz|VhyVTy3fQ0%_m)qYjVR9ENvkAJLMcXkN#cHq$d3LPHU~R~ zRl=Y3YvY&C(BTG z*mExku1q%5Hm2-=#}O*#x5wo>!j zH`Rc!u=F>3lJ-=kX;q99JrSl#{KK;PyR>=a%5W`-@ox=t;;Gw`{8QNjau8@JCk z@^oODn!jnh;SrN52bYoFaI63N>(02Iwsr--R`$HD`ojz>jcRH-(sIAO=rmgex+O@a z$$R#7r?%=OB164o#COL+O1t+wyEM#Y=C^(=EAFYFbKJvN+N6*pNdtHQ^S8)-)+ z+M$)6@D!f}rJhx68Xc+GST&ILG@S7fB7U(Vy8i9!4fi#+%KV{_x&DM%&JD3bbL{#1 z7s#2&t@xbwu8!ioTI__FA4#aG9D!ox^O9+0KHIFje9S8is)|)xMwyq049mS+LQUMP zW6Iz3X&=cY`75&CWplhj;|B(H@gvDHyib^}S#F8gtDUx+Uraw1CzFFlhgl-DtV7`^ zpwU=oepS7m&30!G-3KcJ-~h5Lss;cD0*TMS{uvLOf%es>Eq$ojxql-89j<$H-TTU7 z*gY@#z3rE9r8AN=WMe2=BR*{1b(*F*{^G^(v@n^BP!@YNdwGv|Y_SEannEoA z?IX+M=D6ap1>6Kp*>EP)=}(7?xY8B!r5ZJpwlv4Ng=qS>+$lrdeRwa9%*<7`s}brO z7<8}_63t@8aglr_%~!Jwiv#)~+9XBzI$FjHNbh z!8Pm1Qr`(c4jN5^(7@S>dL+zTJ9&~N*K_#9wT#0U_Gdnk*`^(+&$|3A@oTE>u4~G9 z&Uw|ji5tH+sWD4b?ww1chtUkvTH@=tWy5zY$u-|L8}eJKzhqcxHSM{E&^bOrXo@wF ztm6=@V+1rVa?)TskGddu`6$%usJjb?F543YYtGuP6mFPX_2t?h9Xg*C_pOb;QBFB* zJJOXgucG45&qM0?1c$&Q@9f{vj*OkezhfS0T(s7hkaM2-n0UCHdFMNbv%hCQ>6oV}Y0g8LTSq#yynyZ~YfU=|agt)!N`g|;ZnyP#D z%5LAO>sKki;gT%zs^R|nW=`qzy(+qF{Y*U?i{72v^PWfuUMO>n|EOe}$?81fb(J{H zWpN8q7F=V;c}$t``2Yx&lTeQi3ba470VJ8F>M9N)Wt;KAji$b`xHUSc)hFSoga)&*^lo;|g^%@9_wPyiOq(V{ZR;@s)E1iW}z4V9>3HR^==@WxeU zUv`seRAu}CLj1T8JKc17upGG>D`-5ee|w=%S@`~`bfp-x;sYU(^Gq>{H}vIs^*v9T zM0QZe^k`vr3#Mi7_cGzV;4&VFZKnqUI|4kI2ix&CGg{ygaA(W(7rl$*2VB)8ZnfL| z8v9BX_)($C^ixq7gWH&}^>|}_CRN$7`FPr^Gl+?x8(?qqHEJtClO-kH9ui8SWtrzZ z-REh|m$m1-O$}$_X}7*_eQL~ixsIXUl)s?K&^FFa?Z{vG8rwA=ikd=|$;2B9xh(?x zDVbjSXVp+&mLB9$mfc>ZT#YkLV0hN_fo(`_e+ptxx;(lC3L!<=N0&2e`KhxG14l_% zHIQ21mPr!$yvDY2>-W!YDF|LWX32EA$Sj#Dcp$s*a}?;fF%wpGqw zAcdX;qy&n|S{D4UU&|SzO_Lb3h9>5P!t35e4Ec=Zmu?ZOqAwrbX8jWXblKd`QT^|R;SZZy!ZTBgr%yK%-`jU&B_13i_!r-wW57mTW z6X%f32e>6@>#gY&jx6p3zP(IjdRiz0ffd2$!J3x?>`Xic(6N4Xk84p!t~Qd!1?`oc zO)I>(4dcq;T(VGaR*qgIwg3DgjbtWOw(9{lIc=_@vC8Vy>=}bo zuk6@8T=-1%3M#mCEpdiga1>P88bPttZmN4T7Ilg-0)mTH)0@cj3thf~81)@HRL`^- zMr`fic}Rf~6ihl;{dGb4V#hmPfS`69&T(Uz{8bH&{kLO+ZkEyTMzLA)#^U8(pRi4PPHwbUzgl72%L*&XoyL{LJ&jUyJa88w4;)48QIeWZ0UWl%1jkix z(6i{lpP0sQFtRM3f1{zgXL6CU+nZ%x=k5fxYTzwRlN$6q{TK5>Et+{D2zsuB{_7o) zOjV--DoHoYMiTN&q0{+<4HPY?^gOy%FS^{sP)nTp1Qn)z(MvTR8yl>xxmthE!(@7O z)!Y4jOF7YUl-7KSBXoUaTPu zNasCT&31D%r9?}+Biz-uZZcixjA+d|wtPDP51NuKPKGU31MKm-Et75<@xFP$VIIO$ z;M`~T_E$M<{w$#4M!1YfU$X#`L6GxG24WI+3CdIN3#Kh&2rY9IWoIEv zu2|5$4BDR89#Bl9IXgqXAd>3QIwg7Q$&a_(_sLAUCpvIGVZA#AzSsG6X-aFFXcEQ* z#}morc`y9R(*UoJz;;lkNxBy(U0{ez@f-FoLV%N0%5MM&jM-kV7KH0~hfdm2+9TCW z-%CfiKU<=(JJymeNOES&4kxf?Q$UjouI>Vfd{TBwI+QMYgEV7+$ALrz3I3mM&(C2$ zOH=GgfxQlSj7mVwR9|5GK(l?!c5T=~*WP?h*r9dC(DAiq?G9{qc@=*4d z;en6aUI?s1#|;)#pN?I@R-q}_|3GQ%w{M4Z`SH@lUfJJkZ;58uLIafYt{RfOXoN1~ zZLVRDDrFsByE`SzS-1miI$<|Z@8)?6yh&5n5IX)`Zc9yT{&-o9OHKciIkouHrH|Xn z7<&Rw>eX^j>oIbM3!x3MlbPVL|M-a^BamxUjNlcJyE=EUYrb@l0btupzBwMrSPI!s zH33~;5bFDS8z*$ls9)k>sq*IEvs<80}Q zu2@&AoNoxVhnnt@bzFDdkh$d>m(?+X=8PEHs^n(_mhNo;djZH2Xrtu}%q~O7PE=#_ z1gocVZcfUHKQmP5(Q-S~=1QjrBTr^QXUvxO`b8$eBb8POBA06LDW$|aDws@vYpvXI zflI^jS3L?m?RMy(fCBN?#VFc^EX zY~urzdzQ?aKTRn~nb2V%RvS7l%0!xZoF;knn{c~HY$4L11!sVOI(Nn132Gf6O~kbB z(r7{JMeUk$dig**9Rqa3Pf5v@)5FB&d9v%|{KJ<3^ItaKh~vkS3kN1m9#DIY|WS0>S(kv*WQoOr*4dINz-d=Nn)qYZu4lEUy*=M`fW1l)UA3IBE&h36MT4ie5xu$@f-?1eEs1L4C2IBFSuNpzKLkpvxR zf}(iwfy|<3fKBi%H6PxKFV9uvRM`(cT2pKyr;yEuc4^A8zu+7cgTw|fWA5$afnQ@M zIRj-QUneVil0!WKa_SM$WPiZc5g3jkrd2bONc`M3xoh8(vxD)awQp{oWyIb&d5&oF z?i<&k_4*$(Hbu{!j(dw%OL2Z9ZWS3iEY8#q1R}w~Z*qM1Td&pkQE#cOMr5AePF8S!lGILSdgK6uuv zkL#WAP1b6D3~ew_E;OD&abz7>jbbJk%7~7dJ%_o|K3#UC-b%B0UfaKR8M)2jd0F@e%3fGqoJ7cHIH^I2J)9(G8#aemObYF# z6%83(`y6|%cPk*JcZWWMEc4W(1u^?uE}kBVw790_UAkYhNP>fJ5i1pRP&93#YJK)+hR$x@yk-Ob6uX(bDXV6eOk-~1RW-6=;3ibv z_WnZSP2}8BL6p8R!l?Mp`$W#6qyAda1;BuMwypYcc4rh9P~JNECpESTtM~=S*qy+m z!!MVB;13xj?*-MYTN;_?@ zGtA-apPhJJ<;ow5ll~|K-eK-p(L$qRFBE1nyWPm$4rDhh+e<@Vyxy z#0?`bc!7&-Q71a5m;d&IU{m$+hcPeSY^R;pF0kjvbl1N~arVNBLk2w?qjz0#C?(77S;D#(`o~(fyhnPZ;eJh*Sr6`j9i+R!#xy)q4)YJr}T!L)f`baU)aHUe7B$rqVvh_|xH9oE&);?D> zV)j%ZC?ok>aI4YR&P(yKwdcL6$}VRqHB|HH%t&kS`;@dMeuKC|sKeoGN?3vpZaCKHXT)pt-iAK`c$eylQh zEmAPyl2afHuQi_2HO2nVFL@ETdDRaQoUj3@Wr6Yh5qtnbdCIMnSm<8Rf@%eHeXVTK z(1(LHs^%)e*&Q8LG6}JlsnNqBc-KIEt-|yZ%>{uG{+cHfsipg5S%S{*X{uy<9h`3K zv@)diyg~^u6a&A!P(9V^rvimrLLFj?586E*#S72c4~gC=BgYJc!%IX?(_E2dQq%Lt zelLsM`DmOtXs1NJwRsmc;`1J9#V{&lcG?-zboe8vTsv;KmadmU&jJ$`2lLY9wc%0AUjgilZ1NP;}TH*K&5-8 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