From 013356bdbe25653a39cfcc6fe128766fd5c8efb7 Mon Sep 17 00:00:00 2001 From: midiphony Date: Mon, 6 Oct 2025 20:45:00 +0200 Subject: [PATCH] Add shapes_math_sine_cosine example --- examples/shapes/shapes_math_sine_cosine.c | 204 ++++++++++++++++++++ examples/shapes/shapes_math_sine_cosine.png | Bin 0 -> 15050 bytes 2 files changed, 204 insertions(+) create mode 100644 examples/shapes/shapes_math_sine_cosine.c create mode 100644 examples/shapes/shapes_math_sine_cosine.png diff --git a/examples/shapes/shapes_math_sine_cosine.c b/examples/shapes/shapes_math_sine_cosine.c new file mode 100644 index 000000000..1cc3b53dc --- /dev/null +++ b/examples/shapes/shapes_math_sine_cosine.c @@ -0,0 +1,204 @@ +/******************************************************************************************* +* +* raylib [shapes] example - math sine cosine +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example contributed by Midiphony (@midiphony) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Zero (@zerohorsepower) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" // Required for: Vector2 operations and Clamp() +#include // Required for: malloc(), free() +#include // Required for: cosf(), sinf() + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const Color cosineColor = RED; + const Color sineColor = ORANGE; + const int pointsSize = 6; + const int lineThickness = 2; + + const int screenWidth = 800; + const int screenHeight = 450; + + // Circle + const int circleX = screenWidth/4 - 20; + const int circleY = screenHeight/2; + const Vector2 circlePosition = { circleX, circleY }; + const int circleRadius = 140; + const int circleLeft = circleX - circleRadius; + const int circleRight = circleX + circleRadius; + const int circleTop = circleY - circleRadius; + const int circleBottom = circleY + circleRadius; + + const int circleTextFontSize = 20; + + // Graph + const int graphLeft = screenWidth/2; + const int graphRight = 750; + const int graphHeight = 200; + const int graphHalfHeight = graphHeight/2; + const int graphYMiddle = screenHeight/2; + const int graphTop = graphYMiddle - graphHalfHeight; + const int graphBottom = graphYMiddle + graphHalfHeight; + const int graphWidth = graphRight - graphLeft; + + const int graphTextFontSize = 20; + const int graphTextPadding = 10; + + Vector2* cosineCurvePoints = (Vector2*)malloc(graphWidth*sizeof(Vector2)); // Points array + Vector2* sineCurvePoints = (Vector2*)malloc(graphWidth*sizeof(Vector2)); // Points array + + // Initialize cosine curve + for (int x = 0; x < graphWidth; x++) + { + float y = -cosf(((float)x/graphWidth)*2.0*PI); + int yCoord = graphYMiddle + y*graphHalfHeight; + cosineCurvePoints[x] = (Vector2){ graphLeft + x, yCoord }; + } + + // Initialize sine curve + for (int x = 0; x < graphWidth; x++) + { + float y = sinf(((float)x/graphWidth)*2.0*PI); + int yCoord = graphYMiddle - y*graphHalfHeight; + sineCurvePoints[x] = (Vector2){ graphLeft + x, yCoord }; + } + + const int windowSplitX = ((circleX + circleRadius) + graphLeft)/2; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - math sine cosine"); + + const int circleTextMaxLength = MeasureText("-1.000", circleTextFontSize); + const int graphTextMaxLength = MeasureText("-1.000", graphTextFontSize); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + Vector2 mousePosition = GetMousePosition(); + float angleInDegrees = 0.0f; + float angle = 0; + + if (mousePosition.x <= windowSplitX) // Calculate angle relative to the circle + { + angle = Vector2Angle(Vector2Subtract(mousePosition, circlePosition), (Vector2) { 1, 0 }); + if (angle < 0.0f) + { + angle += 2*PI; + } + } + else // Calculate angle relative to the graph + { + angle = Clamp((mousePosition.x - graphLeft)*2*PI/graphWidth, 0.0f, 2*PI); + } + + angleInDegrees = angle*RAD2DEG; + + float cosine = cosf(angle); + float sine = sinf(angle); + int pointX = circleX + circleRadius*cosine; + int pointY = circleY - circleRadius*sine; + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw top angle label + DrawText(TextFormat("Angle:%.1f", angleInDegrees), 20, 20, 30, GRAY); + + // Trigonometry circle + // -------------------- + DrawRing(circlePosition, circleRadius - lineThickness/2, circleRadius + lineThickness/2, 0, 360, 0, GRAY); // 0 ring segment to let DrawRing choose a number of segments giving a smooth circle + DrawLineEx((Vector2) { circleLeft, circleY }, (Vector2) { circleRight, circleY }, lineThickness, GRAY); + DrawLineEx((Vector2) { circleX, circleTop }, (Vector2) { circleX, circleBottom }, lineThickness, GRAY); + + DrawCircleSectorLines(circlePosition, circleRadius/3, 0, -angleInDegrees, 0, BLUE); + + // Draw line to point + DrawLine(circleX, circleY, pointX, pointY, GRAY); + + // Draw cosine point + DrawLineEx((Vector2) { circleX, circleY }, (Vector2) { pointX, circleY }, lineThickness, cosineColor); + DrawText(TextFormat("%.3f", cosine), ((pointX + circleX)/2) - circleTextMaxLength/2, circleY + 2, circleTextFontSize, cosineColor); + // Draw sine point + DrawLineEx((Vector2) { pointX, circleY }, (Vector2) { pointX, pointY }, lineThickness, sineColor); + DrawText(TextFormat("%.3f", sine), pointX + 5, (pointY + circleY)/2 - circleTextFontSize/2, circleTextFontSize, sineColor); + + // Draw point + DrawCircle(pointX, pointY, pointsSize, BLACK); + // -------------------- + + // Window split + DrawLine(windowSplitX, 0, windowSplitX, screenHeight - 1, GRAY); + + // Graph + // -------------------- + // Draw graph borders + DrawLineEx((Vector2) { graphLeft, graphTop }, (Vector2) { graphLeft, graphBottom }, 2, GRAY); + DrawLineEx((Vector2) { graphRight, graphTop }, (Vector2) { graphRight, graphBottom }, 2, GRAY); + DrawLineEx((Vector2) { graphLeft, graphYMiddle }, (Vector2) { graphRight, graphYMiddle }, 2, GRAY); + + // Draw graph outer texts + DrawText("1", graphLeft - graphTextPadding - MeasureText("1", graphTextFontSize), graphTop - graphTextFontSize/2, graphTextFontSize, GRAY); + DrawText("0", graphLeft - graphTextPadding - MeasureText("0", graphTextFontSize), graphYMiddle - graphTextFontSize/2, graphTextFontSize, GRAY); + DrawText("-1", graphLeft - graphTextPadding - MeasureText("-1", graphTextFontSize), graphBottom - graphTextFontSize/2, graphTextFontSize, GRAY); + DrawText("0", graphLeft - MeasureText("0", graphTextFontSize)/2, graphBottom + graphTextPadding/2, graphTextFontSize, GRAY); + DrawText("360", graphRight - MeasureText("360", graphTextFontSize)/2, graphBottom + graphTextPadding/2, graphTextFontSize, GRAY); + + // Draw cosine curve + DrawSplineLinear(cosineCurvePoints, graphWidth, 2, cosineColor); + DrawText("cos", graphRight + graphTextPadding, cosineCurvePoints[graphWidth - 1].y - graphTextFontSize/2, graphTextFontSize, cosineColor); + + // Draw sine curve + DrawSplineLinear(sineCurvePoints, graphWidth, 2, sineColor); + DrawText("sin", graphRight + graphTextPadding, sineCurvePoints[graphWidth - 1].y - graphTextFontSize/2, graphTextFontSize, sineColor); + + // Draw graph progress line + int x = graphLeft + graphWidth*angleInDegrees/360.0f; + DrawLine(x, graphBottom, x, graphTop, BLUE); + + // Draw cosine and sine points on graph + int cosineY = graphYMiddle - cosine*graphHalfHeight; + int sineY = graphYMiddle - sine*graphHalfHeight; + DrawCircle(x, cosineY, pointsSize, cosineColor); + DrawText(TextFormat("%.3f", cosine), x - circleTextMaxLength/2, cosineY - circleTextFontSize - 5, circleTextFontSize, cosineColor); + DrawCircle(x, sineY, pointsSize, sineColor); + DrawText(TextFormat("%.3f", sine), x - circleTextMaxLength/2, sineY + 8, circleTextFontSize, sineColor); + // -------------------- + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + + free(cosineCurvePoints); + free(sineCurvePoints); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_math_sine_cosine.png b/examples/shapes/shapes_math_sine_cosine.png new file mode 100644 index 0000000000000000000000000000000000000000..4bac572fae67e2a1f5e53b0442975ab0d4c14ba0 GIT binary patch literal 15050 zcmeHuc{r5q`}fQ=YLFVr9y8O>hKQ)FgJKd9vTuoOA!O{#h^WM*l1G+A%D$6bNywUg zU$QSF`!?adEYI_N|M(rp`~LeLzwaL%9p=8T>pJ)Iygui7KT*4>$Uw(I2Z2BsuH&z& zLm&sx5D4@u;vo2i^=@i4_>cAhUe^f%Igj1>2X*}*Lx4cgkn2}vHC>G;eFE76U0$d` z5sgRhlh}H$UPPg9k3mnJI*1wj1$)DWKX3|miq-FJ03y(jExhOl3VjiWO@ekPCB2GJ zj**bcR~_i{NS!tMJ^feG*^ z8ppl6=BcOeid$bCxaiHVV{qur_@AyU9crz}P;OhCuKzgWxPwbBs&VV?_K-tA*OxEr zu?zfnJ@SHmINZ3aB$hX~Mk(Q?u1Uo*g{CUPul2HH;Auwo+S+c)KCyW+c@Cb}{2B&2 ze0uu3oTx@OF0@^a9x`?Q;4es35OIgWt?Bs%rMrw$vPBK_Z6~*Xj##UH_TH`)%;GDn zQ@VXoGA~8@TZ@%6s;6X`CE6vCZsH>|DI%REru}a8v~AjQbYZ9i#jEXyoY)ZCMVTK$gPSKB?eq)`YX@5xdD2u;P=431BVY~IPLaS7J?xNj% z>bHiG$X~a%jW?^dSI5^fuY^LNFt{Dvw-VPfpY5&Dz9N@PRB;dY-p+=+?$PUV(TSBji5aHVe zDq;Vu!WlxL@o8oKv^!hu{;JH2CMw^yd&&FHDm85!Byl?3<=>4pXc1_Q*F9$cu4-jQ z{;wrxFuAo4XiNI~%isMaH=1t-E95z)h&EuyDXz1vClw_WbMc}LCvw|Q{jDmT9#jHt zC6%l2w_bqujzOW{7XH=&P{zA(+=6~a9#+P~qd7|7;pr36=38|F>WP0ea-FWeDcV#} zy2rMsc%U^IL?<-!@U9u`Y4*%1H1T*`JLBIcf?sv*zbI9DjJKzR>%TVruDc)>8+Rdw zOTMo7g#15byo)Dtkwd#TPX42Np#oqr>o6g&f0fTmlhyC*Jl>v&^$YJIn{KGI-Biw; zU1D~!t79`&R?2f~)lPEwURQbHAM!?NW{9sYbzuLYhGYdS)9Qx(uYX>F|DZQ3p_!hc z$j9}2w|p%&wPqA)(4?FYSnj2~Df_mB|C`1P=8OZTQvX;h60N3;gPc`J#r{hm9MI># z8i%X1y{4$u)AN2dCywRcgY=LY2b$%WgGhUl+VLD77_6GdUrgoSEd&tC9`ssum!vC? zpBZCG%=WUi&nvzXf4Id_VH$nD zqnzg5#$<(m#43~rG@m(bL-?122*5$axqvVKFo|T-$3gxdD1-lZqyMY+{#VIpEh+IQ z+hI~vId%1Sc0{qq>UkR~$3I!gl@CqSVN^kk9N|!WDLJJrWM!($2H!Jvp?AQ1O>XH& zH+Q{rIE6^DHNIkDe=0nu_-WAdJ&T^u#X%+u+!;coESm6FC~+B%7rnpmd??M^i-sh2 z5NX(xqg@*;zJC9!@5#3c$u@(hCe5`5s$VXL?>of<4IHF^_VV;(Y}19Qx4%WcnAu!R zs!Ew2DBeqOq-hW!oQozK9#Vfg)SzgBQr`#(_l=r<-E)?55)oo*CCQ8wPZK2Do?%o- zlMsKow>8@h;4#V3*Y9B;^4#%k2^0$L*L=3O3Ks)|;bWX{NL$BZuT0)~^c8=_Mv8GS zT2MfKrL@I&21ZNaXJaRY&2^$ybLZ;t>^>#?-Z%=_S{S?Saa=rUA*P>M_a;)IJLRui z`|CU4aI%eEU(_f|vbPf9;j?zJpw8H-kI@QBdoiULWb1>*j3K@Om(n+nODz=#>aA$p z!0xAfX*y3@Qv;IMnofU;dquZ>393zS=@5n*mHGly9y>e4(SZ<^krm=wMQn*AC{Yh} zbhy*MX*o}OY)`9(fPTZ*)9WTQA*8457no&vU^SS1ww(amPP|uS&|ij&yYLo>li1rW z2749E8hBJC$|5O#-so{G{YiE;(d&Ek@~|Rtp)bem)#j@0d!3H@;v3sU2)j{FsFY~` zW$M%S8*@l$@pC$(v9l;8pB(RVM{lj}E5{FPdivVx>%yqxNtt&xrux6rnO@$1_Dny6 z!x&DGH0{ep3@6(kwuMea?h4`I*+G^RPIC!5 zRL`lht&Qo9?aAle43;0ocmZPxKVFy#Q3bj1&7TzZm;lOBH?cyXXvogDG|RP@Sw`DI zVg+ZiDU}Q8u-39?Q!~X%P84~a8Zmuy6WjEPsGs#uW+TPGOR0_9yh_7@TNGoXkXo-l z-a-W2`yaz-7(5u>8)|O5?{gOL8Div5M}8>mD9y@1=xCliz5gz`t*kmOmrWHNAO87y zq^4`7!vTTLWOg_Kg{jiyVc=~iV6g(!QSbdsgc)~U%iJviyK)8D=won|pyvX!LFkqyb)3h$j7o?Oe)wL7h|JvPJxA_6 z%B8sBE{9z1BYzI}_3Lb27hQ!EIa$-%%=wlP1#&6ud^9w$Tt zXC5g&LC>8DN*O-$(?47^V9>d0GQ|_Ma$E_EI*2CHmDWFE^cy> z$xXB4Azu}hkCkH1DAZhtqg~^~#NEC~@eDKe>@oIs@rnJw)mPBI(rA)1_t>Y5&v8{h zzv0Q{2ncCZ)RtP9=TrYyxB5u+lPO~c)ak+pXGlZv8NVfsX1$`ETES7Hn4?za9mAHd zyBH?NH$<8<;VWO_mS*G{EoZ7;QrEnE2)L8lSD((^VdY`!xl=*gNz0xVUvs*3*be`! zh&vXY^~aMvzmGg}L+26Ph=!Zu{%+j>2YI5yeD-Tq_j4o@be4<(n#9ybc&bzNv-FAu zY_<=U`uC)o(q#rMPQ1-FZEyiqVk1qj9dnr+oJ_p^V-Dl@O;A}e_$=BlNVlUk+uH3? z{71u(&OUPMatk-93qIo>nf@8F@Qq(_4Jp?ksBRI&3tqk57@2KFfiAz;>Wn$m>Xc?+ z+sI?mF|3zMLq3onIV6uL$kT}u4JIOmZwfdy#``0irv0{n~KLb z5NOiW=#~~+qu-L}IvbUX)Z5V>m3bv!fE0l-^V3h0Yg;nF(d-a)N)d$L) zSL0eac`zgWCkccy*tBR~pTmrcm_oG1I|T&$WLUDAtsj|8jybl*1C|&wz^WTE9z9JDV^3sX*`bO)(IcJ zv<2d?E1n2U+67n@?5kpT8;1C2LU+2I55q(8ZPzP<*;P*E6GRoFv3>|t%(+QI$_@dk zbO_WWAC~yXr2CgGiXhi4NDx@THvJOq3h9@`s<6xXg*0|NM+!IQnk5jVmC~V5Gi-mi zEm{sgSu5+4OQTnG(8gmzhM#;A>{`_zhj(QVH#LAtpw_J=ydYMS?a7MfEOHA1-U9h& z2enefrkfW&&H5pSum-vqp8F$#^KieAV$mpZ;*#Ur0A%Ru$tL$AScyR#g|0y)iw|>k zEzlU%gUpJ|?eyA?+4{9ClOH@UARdY<0xWKID|6wr`nm0>NVx^$!|s-f?*eSHxCF4e zG--b1(caCsHt$1dv_2}5-2TTYz*a&JIBs(ui`G-)#OW655UQDD$<3c(tr39qxY1)xlUK*KydQUHP zwAZlbi&PnLKF>ys97A{!U!;#0=tZ9=2F;E2-K;iXP3-bM&NkG2yEY3(MgEFi%-V9t zHa#ofkoBLtm1f#PLymTl$*Yw^oOO9Ik(fp_1W-!c(s|Dy_3MjhvKDR)Kh56LtqF}^ zE?Z~R&#?_$NSxCZty(=ZX#Z$QCs*uy@%Ggg`ElFU$}@-@bVk{=Z2t@O!jG+B)t~HJ zDSma1i4o)Hgr;&+zOKW!7EoKux$`oAhKNL*P9+3I}PBhajQkM@M|pY-2G71d(olx;)A_2HD)IWTEg{IuK0+}H!vhx3i3 z&Xpn^$3q|39o_MnSf%CfY#TSm?JCb{aAM+vvW*((*U-O;b*DrgA3-N(x?8IvCKL6( zD-m($NS&8TGPWp{}Hp}UXAlj7VKFztn zZSOnX{v}n%d2@LSD9+OUnrB--xjEQ!g<9Ex6fTnQzWpJW1rDEKNos0mlgl@vC>G85 z^4lwi`6w3hVhOAhXW}cPw-*-r_^c+@ybR^4(w&)3_7s)|J;o`K?zrI8PcHX|U9v9G zZd#*7B4{HSP&OWRm2@>5S4gEaGu}D6Wf&LSJ+HMtT};`>n#X7?HCN&7&Lswmg!Nfn zacV&0;-Mbeg2D>MZnw_gCXrIoITegW@}~7n2oSe0vFAneIcE!eaSIe6d4uqsoxzk+e6g@-kUAwu29J) z6B=oYwTE|=xmkkww%aUVOTWH+2LL#!9;?L z-e0Qe!+{LUwZF3{nVI9uQ>MMnz<0%GEj+j!4}Ginm#_+=bjmvRy4dUyb%px%~XrwlvkQ*Jel0>=I6!7M<)n0DWe3Nm+oc-1R3=Z4G)kLea-#jk0Yhk zt;g(b!nw$QG&h$XEMME4a@(wCI}3%C(7&QX5oFzs&wf`W;%Z^?08CkHD-vMs>qG>d z%b4~3%>FG8_@RLhy{?3mR|i`|l3yp{7u9NUzGTa`c)WSY~`m%l9Kq*_16U4a&%M^kRB zyqZ#3=(^BDbA@lr6=n_?d$oMC?JXBsGxR`i_G9GHpiG|h!vb*p-{)_s8>msW5kSpb^OXxlbCWoca|n@jJQ z>*z3^3O)6ZUfaMX_$2P#Argjwb6<~_Ua1sq6YNp*p{k~?RF!Rckq5l{D#XPUVD@mh zCH=`3K#~+}6+YwLyO`{?f`rD4z2B-u{F$kuYz_B&uW!Vq$DCLl(hl?d%X-ou z$3fCWRmZbG*K(vy@p<B*tRY{3MVkTQ?0xXQ&1 zicV(oxgm$1CPuoW7i>&XrgFBYBA+JE(Wexlw}jpAtoiljvPjSP^6;l6AdumD?|LA@ z&TZ$EOGh3}TF6!DKJ^-`?&|2uyb=uosF)wwQ#6Y{w2=@WM)3~1r2;hz5+}$!(~~vf z#LomjEDKULL6h1EG6&z=x}TH7w~Su6466ZmXtP(tmtMKE#%oPnPHK(oW6ZaFUY1s$ zAw_Dh2rwkCvMvA+vixyv4-3ZZmpLZ{Gs>eTUF0;76c-z({45gSIWXo=` zHnhs3Xs`T?D5GS_g`7|=?^syT&St{0NGqLrGaXk8Q4dyB4S~=4k(U{~1SRNga7=Or z^za{uoKkA$xwRg&TgM}0XovPR=#3!IxqMAl+uCx~gpRm;s~(2QRMYP_&}Bqq=zz{q zI1980HJo|?YE~S<^7ht!#H4MSCmr*O;9tFV$8Z)MHUB#m5p_1DJZpzZUJ z6(_m&8iM=;ZA_YuVu@Onh2a4#? zhLbmiJL8a9k3(*;_lAjDo7X>skhI`4zB%eo#D|g6Q=i{V+=iTum6QdAh42pn?F;6M zWcQ%Zy9i0v3Fpcn(1U`m?#i7$JC>hg2&{rGQCTzn}wXA$zBvDbWXN|OOxZMMzS1s2hvqqi?>bc=JX7>KQBbZ^WwmL*SE1Q64C(ZRTg`CSDiG3mS)1V$O9zG`$DeBir zlbF@}P>EO$hY@5?omn`laa?-rQ#M3cF)F!E9g+AoO#^0#2+E^ozVD%_Df;6_E-uwO zt)}$uE#%jFi299_wlZ>fJ=Ii0*0~ea&Jq^U$3Qvlb|s?x959jS+$I&sUmtmf>opPV z=?V9)4hENhDnhv^M{)|9u=4c(cmdY~pp0H>oyjnh+(EuilSuTOX8HL*8o9wpK&X>Z z2}*#tWF$LmZ&dzhvi?mqgr2T*^Gu-B18sur!SiW}Dww8gkoiAVY&H(mm~>Z#@@FG(^%r!ubGr z!AF$S`dhX?vIs3NVEhsT%!`#L;s@y^qH{AmzUffQaokv%&#~+n0l$kRJ0zTr7H|xg za=QfdV+=_ch{7#GziH1A@)OQ8xF#S?Sx1_gt`kDjf2k<2-^Z`H-Y{e3`D}JV=m`+u zb-GQni26fF-UvmL1f;O)?fO?ByO{%fedSQv^K8yR`$5nm4f(><UX$Wk7v-UYZwo}e*r_kd|L4~l1*yI;4(fvy#9_6ME>W;zY0Cw zVNap&nj#F(Y98T2jtd~W@!5<4ux*nqH_NNO_B`HrS-5*0Y_V?L-xuUho>jV64o6%? zbkaP*IaVWK1ewQY?lxaXps!yM4ZDFu)bJ)7!X`nSj?Zpw_5#POGHB$WJ>%6*q%-Wq zhlrg9(NlT^s-v3XYvAO>0)=_ed$z64J@-CEa%MH#ywNUF-)BG@k96JM^3oJZ%RG}{ zc1QqYw*Ja>-Gzath9jmp)|}OhwyL&lI=Fg-=h{d6)wnY*+EHE5$s~h=65XjfrL?sX z+_L4fRfyXsT#ndPa^&aT3%hB;Fg2;?*{|Lrfu8>8Ed87>_r%6T)lEFg+1l;ZG>4wi z-Fu#1S0bEdj$E(Jr8wUQX*o600R<$KAr?&wx?R$R$JuO8$Jd@@yGTL-4s{Mn0WnO* zDt*_{Fey+_>Q6XBY)#24wWmd3^4B7jiMYEkZoGX8`mvKPAjZ7V>gN3omK3WbllO0E zdV`(+YIp*-qyMupZd{gkYe6Hs=TUok4pTXiiA>L+4@B)4J4jbE#5-*J4&m-)PH_ zGDYDu1j6xPXBMC%cRD5>R#z-;eis) z`%jicqDEW3+8b?od^AqB?t5iqMe!*#&>PY7F)Wcberp<)?Y(B0HIpESiEop+xwdS- z)jK*(DJv`X=!5DK0L(gK?!;+VcV z*9F6OetL^FMS06~H^_K+&kh%nE~&wyXg_bJWc#w)u|SP$-}>K4N4DRT__**tH3rA)mZoewU9ychN}PE#PO@ z3Y-aYz|3eD-~TRl(@{&V$(w}daWJEV;5B<-J$Wsw&ym}p*ngamv7-9 znM`}T48x{VlQWICey%-?;EYB#8(dG|ZtCj`;hyXDZIgX1SN1?%M#CYmtZClkSJQmr zc#G^4h8G~B$%3mP&65*q`0`mr%mk?@^MLRJ2bKM1N#HSHsN*A_ieVx%-u%7<+(KF$ zPwJa~M*MQB7BwokNm5P%dtFFWjtC(I)6DojG$@vRY`1!sET_rb%j7JLsmz zKm=k_LeppNb#3ZQ@e%6^f7HL-0YeLq&*9wPm~)yo?FOS`FF_OW%p=)-2vq?1sG9-J z5cc;}q}A%(7fXln{mYskmOCxZ1{*1xHX5I1_3)f=xZMQuFdzXgN#A@c^YCjN&LnRx z^Fb%nL3dATM`(JVzD~C!$o6-4Z&v5G-?J@P=OVj+{(CVPn^+zgEu)U>pqB(Rs3NOX z15z@YZ?h=@-HT2zZFpEm=qM`vwDwY@ya_jn*N;C&S#UdzWES1^*6J*Y-9-lUs`U&v zFuRQoc90*;aBU-;p!#6BdIVtqqiSvYef*{pqJCzf;GLFZ;m)fSNANAR&4YWo({feS+g4V9yR@0)86t~N>+PXoGIS3e=-W4(|C;WwIL8fSo`zVX>*up>0Jj3h zZ~?zh?%aS}E*i4r4P5f>sV4luChxXof4@KD-KrLo-gusX8{>P%zTeJC1x{g=deZB8 z)oj&AcUrVps$tw-FGv*_feu<5#5IH0PW`$hJ6Pa2t|6=)VAl6(B)9K?Mm$;0q}31r zO_fuZ?J8!g^xxXS@crho&O7)!S|}LUiG#J(sZEe%Vmj0ylRk!TLA3#%tto^2lz&E{ zhbEKy!amhYUMegR!fuR?`Q4b=}(FdLwP>x`6gU^ZMGW>zMfUY{d z^);f`?m90Y#JC;NXgoJL}B)ZC0^VGb`uBOF-^UTeE}J+LP{e(U&;fGNq0a@axo+krnSDRGGn2b#nnZk9;7f~YG1HBID6h5 ztJlao;86il**u*N#7>!DiK#)?)?!2_pCh6 z9C=5A;^?QcY)Vz*{v7jt|hRm+0pZbRO+Dl04U@pV{mF zI{lPhgP@QZq^2?9(C!{W>E#P!t33mo^|cEz1cll3N7(=f|7^dNLQ{lt^Lo=Ba1>q2 z%(s2A`2dLnmPX;2+KAL0>^dKa4z)8+vG;Dt{q#Wu_O*&Vb-D@wkCH@huUnU2;#6T^ z0x`)vMT-90%)@UdTC!yuGSiFDvC`mPuvTQ}vQl^FK(#KSYCizOt?B&iDwzOo zep>8s7<+wq$zd*>WRUdOdr>}MsQCvGub3}qXFH5_W@S&r<;NkvirQ}imO5MpHkzPj z61uC;-Wck*W63wlb0fj640IGv%}s=%jSJqEDt%RSZ&7{=fAL+K}G1j?={cAdC2h1+JxhA+hWIP_> z8rxO4oP=C@WCx&ZyN*x!)l16g4@8?Q9}>WzhUIOO?WO{K|7I;EfU3LYYRzGoNHV?8byz}lii8U8>($ygg6+?1kMt*?f}31WW5< z`mupVp3Z;=mu(dJcr#!_U{8=t<7*S2;_QnT?yVP2x+lyz3<%w(Z=nGWlo%FFK#Z_v zP+#nzE$VdlD*};@Mq*|x8#gA$Jt7wm-%0cC`*;}LqpDu=C9w?goLG(}QF5(TD>yJs zAw;BOs#ee3{EQnXW+Wm#pdr0rRNMwMKup}_U)o%^dIN?7JFEeWz<36O_KVaAW^C z6=%qNjdjh31VPw1jX~UaeWdI=UMOb7O*G9fB=;JIr?^?_A-7nP^S4&0ne1x5thIpD zCX*}wpuM*aJAh7*a@-w@H$hAAYe(Q5qgayLwSOy9%(^I7r@E+ z&MJ)3e&k`BYipL<&Q~E_00J^TkL3{xAnK_lA_bNmKt2Ili|Y@)Xi=NR?FjTy>rpYXH%rEgV9a0yXI$_- zS+s&lJ2F%RG~n#ZC_hjfK?O|H+*soMKYc5=TrNyS5p9|{&_V&__c8HT*TS#(b*@kR zc9w^9wbiBSYzmlSXZ68xULB1SvA>PK?a59e4`?ml0FR_I@8Ze*SQ8j^h#l$A+*Lrvd z>H3A@H}DOZyER2u-x30G94sth?c@9)`Aju=k?wx_*+#Frq9b*Y<>xYvVtOw9mO7V< zl+Lrm`jfrp=E~hdw5w~MK^EfeAf>z0LVhxS;?3|Q_q)i44gSfnSDI-sEWXy>D1`eo2B10Y!|4X=9nI| zK%qx7j>+NSq4Eegfa3{os@308$|%9D)%O@DtS-fm8ivFzXC1`|$h?~kOTLiq4&}*8 zlf(18sz6AB1j`R@kZAyr%MYBFi1mkWiqSS$Xqy zp?j z9u@jYwQplW>Up_zU`ndA$4#><<8@9v+3*lWiIdaYF2@ySwQfy(=Ib0^hgH`X(qb+_ zqogB=a~BM*s|4a$VN~)Qzlq=1sua?&Zmfp)-LR&gmAtQ=$uK<32*U$?f9f$1are2q z4P3#Iw)r0{FDk-N-fb)Tffg83TD08vJj`l`fHX6Ck}$kAjs&2j7XgDD{EQ|L0-7r zI*Xm;HODsjz3@HsTmj6Ve#%BbDa24)I0;~M+tw<=!Fa82#5PdIK#l~_7> zre;Mjf%>!=N+&*ld?o)__j0(+g>U^gtqtpP&)0=PNUo&p^tghM5R}^nmO_lbw3%`( z-;5d8eoYISZ(u&!c6qn>4OaqpsxOYeUF1F0b&$r|0K#!}{>!fVScp-+sZ1g#Q)N6T z;gA@l`+&=mIET3Xfb-Gx7BGClqWnW%k~LIaL%#qRD!9b&4XiDKfi-69*<^RVchd{6 zi0PeDm7^wmrTw5E5#k8r8;y;ozn++w`U)1kicd@w*n4Oq2r!KwHea1Euj$`WFTv~z zve0{XKqFFiW1WAn3^fSf-&L7wcZjFq?DQ?Wnjb%s=MjLFYv?QcGr9PaCWm-vhc^{K z?>eD(RK`O&XO%ZcO|Lvn(aK?SXQHpX4rWpIVQOYxsinFUa${)~c zF)Y)2&B`S956C&iUjL|!p0kYVTn>LXm#{ZY%)zqut0Lg&z$Hm(c@Okp%S=_Rsq_3n zYFvnO^J;B?{nzOtZw1vIqo@LuO+9e_ERbCBp7})9;4P%S_TH7D+zJXExu`2~8gY`+ z`riKqf?GiI3)?O~5E7XPVPcvZ9QVZ^y7uGp)h}3VTEae>G6EQJY<6d$JGne#v&8S( zs@8FOS3c+m;3I|tzRtmQU;LwM*Dv#Y!D13X=V)i1k@O2z`Z)ixTfq@8XZtJDOj7Gg zyCng51lBr&aqJoagM!UXyK3P`%IS&HtUN~&tPiz$0pn~_WT&t{r+otSWnQKPE6QGF zv&vp;-11Ev{CTfF=;>}bj0^-5jK5MJUv{J(By1~mG~i2^=((0>mX&wU0OcJL_rv{C zo4zvJ{1cD<(1OZy>FMqJOsxRGcg0%W{Ho%eZ)x}qM+DcTO78yT_7WCNT+&E3#WE{2 zYtaAagR|g)ZoX+8i$WTB&_RjZpQQ`}u^~vmXGG)~E!O5^wv@!e3}&IyOe|#Nnd+l` z=?lSN)j}WmC0k)!V(7Tf9qp{;`+q%m7ko;M!q$F zy}J_-H#utcU~d-yxWe#vY1qv#E5>Z)vna!#-_nY7+rNL7AUx-XDgu6cu%|c#^F0tl zjD$U)YTDhQ1i-^=zKS0{yMARyZ%b!P4#ig`FE0y_X; zmJ$pm$Lren_n^Hnt><5fhV_5{xufDi5%6)v`@PZCUE1D-gNeZ)J;gmjcQu4Sx9@3c z@3l%GbJJGwe?Nr40ch^vp1tfE)c^2>1uRf>&t!IA!(9MF`)s+v|FH&%OW8BVz1NCo~wN>g2r3( zAwH4X<#l^0Wj}(=Q3t>`hPAx@YXSceA7BMErJE}~Z?=!p@;pD_r=b5Ge4Gloe(mPf JG&vLB{{scw(1rj2 literal 0 HcmV?d00001