From 914a1af967c6b59d2e70d081aed278f7700f3000 Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Thu, 6 Aug 2026 07:55:45 +0200 Subject: [PATCH] Tree Gen 1.29.0: LOD-Kette (Punkt 5) + Aufraeumen/Pruefen (Punkt 6) Punkt 5 - LOD-Kette fuer den Stand-Baum Haken "LOD-Kette erzeugen" liefert _LODGroup.fbx mit _LOD0..3 unter einem fbx_type=LodGroup-Empty, wie die Fels-Pipeline. NUR der Stand-Baum - die Faell-Teile leben Sekunden als Physik-Actor, dort reicht LOD0. Blatt-Reduktion ueber die DICHTE, nicht ueber Decimate: eine Card besteht aus zwei Dreiecken und laesst sich nicht vereinfachen, man muss weniger davon setzen. Die verbleibenden werden groesser, damit die Krone nicht ausduennt. LOD0 578 Rinde / 170 Blatt / 1306 Tris LOD1 477 / 118 / 912 LOD2 364 / 66 / 575 LOD3 221 / 28 / 302 (16 % der Cards von LOD0) Kugel-Normalen auf JEDER Stufe gemessen, nicht nur LOD0 - LOD1-3 haben eine eigene Card-Verteilung. Am reimportierten FBX: 100 % radial auf allen vier. Zwei Reihenfolge-Sachen: das Rinden-Decimate laeuft VOR dem Verschmelzen, danach wuerde es die Blatt-Cards mit einschmelzen. Und der Ursprung kommt aus LOD0 (Unreal platziert danach); alle Stufen werden um denselben Betrag verschoben, sonst springt der Baum beim LOD-Wechsel. Punkt 6 - Aufraeumen und Pruefen Blatt-Streuung ueber alle neun Presets nachgemessen. Der Max-Radius-Filter arbeitet richtig: 0-22 % der Cards landen am Stamm (meist unter 9 %), und alle Presets bleiben unter dem 2500-Tri-Budget (Maximum weide 2246). Messfalle, die ich dabei selbst produziert habe: zuerst hatte ich den Anteil der VERTICES unter der Radius-Schwelle gemessen - beim Busch 95 %, sah nach einem Problem aus. Das misst aber die Vertex-Verteilung, nicht wo Blaetter landen; ein Busch besteht fast nur aus duennen Zweigen. Die richtige Frage beantwortet erst ast_id. Der Kaktus erzeugt regulaer 0 Cards (kein Ast duenner als Max Radius, botanisch richtig). Das lief still durch und man stand vor einer leeren _Leaf-Ebene - jetzt sagt der Operator es und nennt den Regler beim Namen. Panels: das Export-Panel trug Stand-Baum, LODs und Faell-Teile in einem Kasten. Die Teile haben jetzt "5 - Faell-Teile" fuer sich. Neu: tests/test_baum_lod.py. 22 Tests bestehen. Co-Authored-By: Claude Opus 4.8 --- README.md | 61 +++++++- blender_manifest_tree.toml | 2 +- dist/index.html | 6 +- dist/index.json | 8 +- dist/stylized_tree_generator-1.28.0.zip | Bin 33772 -> 0 bytes dist/stylized_tree_generator-1.29.0.zip | Bin 0 -> 35207 bytes stylized_tree_generator.py | 169 ++++++++++++++++++---- tests/test_baum_lod.py | 182 ++++++++++++++++++++++++ tests/test_panels.py | 2 +- 9 files changed, 391 insertions(+), 39 deletions(-) delete mode 100644 dist/stylized_tree_generator-1.28.0.zip create mode 100644 dist/stylized_tree_generator-1.29.0.zip create mode 100644 tests/test_baum_lod.py diff --git a/README.md b/README.md index 0a65a5b..066d4a6 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ nummerierten, eingeklappten Unter-Panels in der Reihenfolge des Arbeitsablaufs: | Addon | Unter-Panels | |---|---| | Rock Gen | 1 Benennung & Groesse · 2 Form · 3 Detail & Cleanup · 4 Shading · 5 Vertex-Farben · 6 UV/LOD/Material · 7 Batch & Transform · 8 FBX-Export · 9 Textur-Bake | -| Tree Gen | 1 Wachstum · 2 Blattwerk · 3 Zusatz-Ebenen · 4 Export | +| Tree Gen | 1 Wachstum · 2 Blattwerk · 3 Zusatz-Ebenen · 4 Export · 5 Faell-Teile | | Grass Gen | 1 Form · 2 Haltung · 3 Batch & Budget · 4 Kanal-Belegung | | Asset Utils | 1 Vorbereiten · 2 Vertex-Farben · 3 Benennung · 4 Pruefen · 5 Export | @@ -385,6 +385,64 @@ Zwei Fallen beim Bauen: - `join` loescht das zweite Objekt; jede gehaltene Referenz darauf ist danach tot. Die Aufraeumliste fuehrt deshalb **Namen** statt Objekte. +### LOD-Kette (Stand-Baum) + +Haken `LOD-Kette erzeugen`: exportiert `_LODGroup.fbx` mit `_LOD0..3` unter +einem `fbx_type=LodGroup`-Empty — dasselbe Schema wie die Fels-Pipeline. **Nur +der Stand-Baum**; die Faell-Teile leben Sekunden als Physik-Actor, dort reicht LOD0. + +Die Blatt-Reduktion laeuft ueber die **Dichte**, nicht ueber Decimate: eine Card +besteht aus zwei Dreiecken und laesst sich nicht vereinfachen — man muss weniger +davon setzen. Damit die Krone dabei nicht ausduennt, werden die verbleibenden +Cards groesser. LOD3 ist der Impostor-Ersatz. + +| Stufe | Dichte | Card-Groesse | Rinde | gemessen (Rinde/Blatt/Tris) | +|---|---|---|---|---| +| LOD0 | 100 % | 100 % | 100 % | 578 / 170 / 1306 | +| LOD1 | 60 % | 115 % | 70 % | 477 / 118 / 912 | +| LOD2 | 30 % | 145 % | 45 % | 364 / 66 / 575 | +| LOD3 | 12 % | 200 % | 25 % | 221 / 28 / 302 | + +**Kugel-Normalen auf jeder Stufe** — LOD1–3 haben eine eigene Card-Verteilung, +die Normalen muessen dazu passen. Am reimportierten FBX gemessen: **100 % +radial auf allen vier Stufen**. LOD3 traegt 16 % der Cards von LOD0. + +Der Ursprung kommt aus **LOD0** (Unreal platziert danach); alle Stufen werden um +denselben Betrag verschoben, damit der Baum beim LOD-Wechsel nicht springt. +Das Decimate der Rinde laeuft **vor** dem Verschmelzen — danach wuerde es die +Blatt-Cards mit einschmelzen, und eine halbe Card ist keine Card. + +### Blatt-Streuung: nachgemessen (Punkt 6) + +`Max Radius` filtert die dicken Aeste heraus, damit keine Blaetter am Stamm +kleben. Ueber alle Presets gemessen, wie viele Cards tatsaechlich am Stamm +landen (moeglich, weil `ast_id` mitlaeuft): + +| Preset | Cards | am Stamm | Tris gesamt | +|---|---|---|---| +| baum | 85 | 19 % | 1306 | +| palme | 246 | 0 % | 1710 | +| busch | 118 | 2 % | 876 | +| tanne | 176 | 6 % | 2244 | +| eiche | 172 | 2 % | 1772 | +| birke | 133 | 9 % | 1918 | +| weide | 297 | 1 % | 2246 | +| toter_baum | 49 | 22 % | 838 | +| kaktus | **0** | – | 740 | + +Der Filter arbeitet also richtig, und **alle Presets bleiben unter dem +2500-Tri-Budget** (Maximum weide 2246). + +> **Messfalle dabei:** Zuerst hatte ich den Anteil der Vertices unter der +> Radius-Schwelle gemessen — der lag beim Busch bei 95 % und sah nach einem +> Problem aus. Das misst aber die Vertex-Verteilung, nicht wo Blaetter landen: +> ein Busch besteht nun mal fast nur aus duennen Zweigen. Die richtige Frage +> beantwortet erst `ast_id`. + +Der Kaktus erzeugt regulaer **0 Cards** — kein Ast ist duenner als `Max Radius`, +und das ist botanisch richtig. Vorher lief das still durch und man stand vor +einer leeren `_Leaf`-Ebene; jetzt sagt der Operator es und nennt den Regler. + ### Faell-Teile (Punkt 4) Kein Laufzeit-Zerlegen: beim Faellen tauscht das Spiel die ISM-Instanz gegen @@ -641,6 +699,7 @@ gefiltert `.\run_tests.ps1 -Filter panels`. | Datei | prueft | |---|---| | `tests/test_baum_export.py` | Stand-Baum: ein Mesh, zwei Slots, Ursprung, **Kugel-Normalen im exportierten FBX** | +| `tests/test_baum_lod.py` | LOD-Kette: 4 Stufen unter LodGroup, Cards werden nach oben duenner, **Kugel-Normalen auf jeder Stufe**, kein Hoehenversatz | | `tests/test_faell_teile.py` | Faell-Teile: **Blatt-Bilanz 170=170**, Pivot am Massenmittelpunkt, UCX je Teil, Gesamtdatei mit gemeinsamem Ursprung | | `tests/test_blatt_varianten.py` | Card-Sammlung: alle Varianten werden gezogen, seed-stabil, Einzelobjekt weiter moeglich | | `tests/test_proportionen.py` | Baum-Proportionen: Palmenkrone, Strauch-Breite, Setzling-Schlankheit, Branch Count 0 | diff --git a/blender_manifest_tree.toml b/blender_manifest_tree.toml index a214939..081a372 100644 --- a/blender_manifest_tree.toml +++ b/blender_manifest_tree.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "stylized_tree_generator" -version = "1.28.0" +version = "1.29.0" name = "Stylized Tree Generator" tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen" maintainer = "D4rkst3r" diff --git a/dist/index.html b/dist/index.html index 9fea14a..f715bdd 100644 --- a/dist/index.html +++ b/dist/index.html @@ -50,17 +50,17 @@ 34.1KB - stylized_tree_generator-1.28.0 + stylized_tree_generator-1.29.0 Stylized Tree Generator Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen link 4.2.0 - ~ all all - 33.0KB + 34.4KB -

Built 2026-08-06, 05:40

+

Built 2026-08-06, 05:55

diff --git a/dist/index.json b/dist/index.json index b53dc80..4eb5a59 100644 --- a/dist/index.json +++ b/dist/index.json @@ -82,7 +82,7 @@ "id": "stylized_tree_generator", "name": "Stylized Tree Generator", "tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen", - "version": "1.28.0", + "version": "1.29.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -98,9 +98,9 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_tree_generator-1.28.0.zip", - "archive_size": 33772, - "archive_hash": "sha256:84ff026fecccddc944a1f847d832f3d8b45b37f142bb9e1cf342a103e2ca290f" + "archive_url": "./stylized_tree_generator-1.29.0.zip", + "archive_size": 35207, + "archive_hash": "sha256:faa1e629b1ee4cfd465db79246a6d4acfae12af61f8b65aafcabcc64ffdcde72" } ] } \ No newline at end of file diff --git a/dist/stylized_tree_generator-1.28.0.zip b/dist/stylized_tree_generator-1.28.0.zip deleted file mode 100644 index cd2af8a9840e62fea7edb1fbd083c7852e4ec2ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33772 zcmV)6K*+yPO9KQH0000801Q0_U2DO}fFS_@0L=ja02KfL0Ag%qZe(S0Uu|J-X=Y_} zbS`vnZERIgQA@)x7<@1EKZHCtZS980z?U)Di(+Ad!jO_RpKV-|j4xl6`Rh&NHnxWl za@<|+&bK`n*?a?f?JRp_{4Ou^lBY?UXf?e(9S8jjsv!huIe;LB-ZrDw5rxZ6wta1D#O&kb*5Gj0Q4R%fW!n*m8skx6Ei_Z?f$@jmC=&jh|qH zk&)U`IDnJaYL0tPv-rdD5bdQ>!r55m2X^uAn#@6z9SMUAq_8 zg=2UhOt$=G_XRESjPYmieY~B5P^{x06LB|yRe8b+^SnBZJLndypAv1;goVNC z)%#*yKZ#jh3Ws8l1jw^AiFU0!98q^YUHDa5-Lp&Sf8HNZO9KQH0000801Q0_T>)FQ z?%ja^0M^_A01E&B0AF8eZfSI1UoLQY%)RS!+sM@@`ae~92W(E|n4BSLlDb&djHb41 z(XwJ$_R+fJOH(Oh7^_Ab0LUxo3^&$wUfQy?C8 zPQ0JJt6(msegc0fIP_=cBAq3{T6Ov8Cjfeu1nKNnB;G4OU-h1U^uv{ide2|w@U*9A z;k8dijE%o~7cq>}@m4|RefDR!X_l|j-c^<_j4uIPVi_l2@iWKsx%XbAxA^iOfBeHA z|L~nx`m$QN9)jTcVd|-m_2^^0(Q(5AEECD|cS#%x zZv_x#9;}ylk!MfvPb$J`nt45M9*9(=9zYXEUKoglNT8vQ-|}HN91O>Mdv@`8e0Fu{ zo!}Vo_1;tTIPI;dw^?~_PweGz6+kRHm0&)>$dFWx_) z^>Bv=s{aqr3`c#5_~Y&O2fN-1Fzgll*z@RE#Wc|VUJsk~C0wrn0|}lz4XM8-dNb(1 zRF7UIegr7+I)+mKZ_3ZlMYPOr;l;4OQ+jo?mX8fEe;&fx(HZuhV5IW9f7r3d7|SWY+$%w= z7vqH-8wP)uL2oAL5$jeFtU!Z^_0yAsh|B7jf>aRd4Z|9Y2kMpv1auQm^d#PK*v{p@KC zX9|e>&j?jW=;8qd4yKuhXDQ6|T>11}2Ru&;8;@ZVg3!B%kHdSoP+(WQMGor)R|aEL zO7Q=<8L~V0cmzuhu;dF^r(8rpj`Oq&M*#5!FU$2pc<=B&v(e@HHkjQ-B4y$*1mf@= z&Z>6;kA;_rkWR%@E|DCrj+G)DNU%47^(vognl$R#D3?!rOb6i9zV-8U<^$r=q4kT?gaZ@= zWp}S@q6H$@R3vz!=w76AFObaO8R+->#vleoF`RAJOfoL|g zN26sURDi=W2YiY0WpNNb=fGuz(}cLKEv}*;hHwF01B>S^m$~SB2f%|V6zqCod>b(Z zDN#)Y+pf74OwZSTk^-ldn0qH%@7S9!cPau}a{~J(@C5bXYe=d zm&mR1nvuEBWcX%Wgu}D_I5(e<#xaA@K33tg97~V*} zvj%&Whf87IOgz{TvVO1!K*A8;-xELDMq~hrh3vJ0=`xNp?@j0@ylQ)U#W0K}eiZuo zqVN4000tt@jEr`b&Do4bZ}K#K&Pe%Bz<$X{BYk{{)COj7FzWP8u+D+*AWG~ToB5rM zy*~mk8-~nl%fkRfVX-6@cr#3C9KnnL6^Z8I%swXZ0@tDAw|5 z@8g1f=RH9C2V^XajtNjF!1i0b8vk_)e-q)Z5z_+X>Xn}cX%a+B*sJ})7K9zdwRC|} z#37ymJV|&2yd0RE#Cz(!VxlNe{>enR{hF;%YT_e^!V8G02@Rj%t=SgI5*WD!;0Ck` zk?QUN=VooLW~_((VS#hZB*RGrv;_%|a4-E4@q_PUxJTgc2N3~L*bFMqSc+0}0MJ8f zh%hq&p|waAJ0>ze2(7D3Px%hyCqZH#iDQT`q3F?lmBLvA+Sq%Q&zB^y z=E&+!!V`qoEZiKh)3_%~ANCfRg`fGTl&`t>(4=(W6_f=l4g|*g6hDd(z(HY-q+7g9 zcudq$%x+gAe9}x64a=ppa`h>y54hnh87z&|4C0?!WL%S5i{eHlzs7mT=+ znqdUu7)5YtCW>?}fiKEBKT4wM{qtopnsML;hVCH&vY3H{g|`D9yyGb`%`Af>7pMe0 zz`T!_P_J!!jOOeheUc*W&rmN*Nd-D>m@1|MD#QzK{`*>x%Ap=Xghc@L0$kP zVn8cc_|;_CIaGsrajda543+uFuBjW4`I`kZb}-AOQ(xk>QSd z39RG607ObsssOJ`D@UD4|Dx~%ha_^1tzMlUUtjlL`^h}@+E;7XMWlSf+v(FhNeqO; z`(gSwahHME`W~@GtDlMimF9-TS+#{%8Qe2)L*w6X0>K&;?=D733Y9LCbKx&~V-TkV z(CepM(G=VcJQsg2s6rvT@AiQQsfz8e5nV%Seqj*5mgAN#aM+I)Syxh0y0+D=?BE_= zSbPYpwxnP~47Rno9q;Di1n0hqwrL&@SIa{xDI_lhbk4ucfQ1BXHGwjurlD44$QzZA zMJnoG-AQ77f=vZ9^`|7|l4$>b<^t#+s+I!5BI6wSvFOko4Ao^lY>hk8Uw`%5pOLQp zA0I(}>cG+gTN@H*{M+yg97b4Q?~zXmOMMn(QzD=u<+dZ(i8*jR6F^g#_5Q#+1)06; z%}|8)cGXWQ?0UVOzB$$j09`uOgft4d@`YDMW)i9R{4$8m3~@z0(5$siLR6zh*%?5gut{SH_YQ%fc+UHyv!0Wci`kOJplSfSOE7mEC(|X zh4@Z4>s^41k2&DnN2z%V908Jismzc7p|l<$>aU3fiM$x0e@j@fiq{>Lmk4h0=& z6brk&j;{)hPlLK)-vMGl&33IBSlmDA4Gr4D-v_2ZvnYk2T;KO9T2ifnoDuR0 zeb}tbkI1w@LK9&24d6R6UI3bRarXM{H67|x=B)I9z+OY+i20cWT4!*J=0Gg)7FA!n z^?~(9bxNXcOeV_ng1J}YA=~#V#)B$=SzQyC4kVn8E0P5GQVyRXi3wYs1Np;SVEUt` z10dZbb2T1Jgzg8XcX(aBN|0jqHC01E%`p>6jwHzdFzFSfC8)#c;j=;=)Cr;=Wh}H_kkff0XgsN>Y4y0 zURGL4H2*|o@Q_)igm|X)!fP`-fz-OAzy(N)^=+Hv1(CI*#>2(t+btm`W?qa6l-)<} zoE9e%z?I9br}6QxOI$4MQlK1ecpq3QMW%=SU1Z{zRE&TG;MIlm=4pdizY8Gm?^YDsj z;)|Tr@V7py@a2&)5wnIOA@wM!0g;m|G=M__txQ?Uv}vb8aeBR;r_7F#aj2LZS;Pql zjc0QK0{<$W=P)g@V0tYP-HR|@1Lt-NTb8yuIFdw#3`H?S1y+3CMvvWLWSYL$q0@rWaVWeBJ6CU zDmEVUB)*n0VXqkz3U%)oSNBk9!!^q{7kNh7vJGhPvJMSs9ZfXYX+VR6ZdLtT!^4&N zp4K!-?h5okC7&PNb;&A{NQt1!`XQ;ocM}FDG7MhYQn^JJMoq}zrVG2>vI%?Ghzz@X z4s1ARq6$L?E{qy)rGwgoJp6_o+^Zhs{qjydpa+WHDMhE%ws>upSJrVFWWjw$p7BCV zbRS|jK|e6}?O=P}V6>$$*G$ZJ_P+TLj~x59+jNHcN>TC}EbO6LN4c<@q{E7HLU$`M z&PIp|9=xoc2z+=?$GZmAKG974wKeiJ$0ngx`$ zjVMPw8w!oI&OO_m$3;GRL&Hg`aIC^zEcS-B3vCB_mpVUgsY!my(a$4INmgau1B^`4 zMdCAW5>?nbTi3PQH=QohwJ$Qn!?{n62Q{us^@^+)BeJX#91HaLNMqKRqatALU@|MZ zu3@2EqftohPVzO3!U`KQ*TdwWa@L{OtCyVmsj}#3k%xMfd!3%FbjWwzda3=YUW%?z zX?1(akl9AFmj=1`$0k3@Z~PC9aew^7Tr9jv8qe<9QH-*}_vlQWDsxz?)p|q5)8V0a z1&xICi@WdNxLNh`%@}~F&OtxY0LYQ*QU>Qd5~#13Fi2q(=lcq z=b3Zj+d&S!H-Ii%6tFwq4ZairP4gg}PvA)B>$ZX=4(C8Jrt7c$xu5xc?8`0E{!(P^ z!ZTMYt^h>nVgTjrnut|=FWS&jb|a;$vuPwAjIRKeD(7(Y&L$^cImAWRJBLA{C3UBc zUHlaW(H;F#0HWUxwF^+Wia&7y@Ez^S49{Zh_xU0yG{7FPW+(Sd0so4Gx*^ViefZ5rTe;cCW71bw1wB8}H1QHa(*yK`R z8T+)0Q=8na{;7sRpy;f_zoSlZF7Urq9s=YSfWEiDu_w5c?|nG;ogeX4Uqu!`n>H;0 zKaSUEj^*di-rt;GpM5<4%QI_KbzBBt`g3-up4|IkjtjmBW1rWxQ*=3RfBH?SuD*25 z5r$ukW~;|qa35`Gy&urDEn$8vfqeYS+54OKCd7}$=PCg2^am!Ov;z9nbdA0P6I6M~ zR*x1^0s2k+=xh6XO$pKxUNNZ~9g;d*1s`00U3cjgrSJtH^Q0R`4-gC(@$N(vW%kY@ z?U)90iX+gUD$xiyJcrdQUIKp6hVC8vwRoJtwcw5EANa7N_biUGAj(B$Aui%bpD#Ll zcod1~1NqR0xzz=TksuyD`*3sp@#gv&NEOs(l6_r^qi47rOlwt_XB~5e{rP+nL_~B^ z9)Ui%aI~SYRnZZGfqO=Om`jhoI2E{f7zNSUMHL|Wgt1I%1%2dii+Zmxgain1M$1xK z#)(ka{#kOx4&1y?y4u)Mklch0?7B%KZXpOaLeAhZ>o zz=phSdku(C2#eb-X3>6&v9}>2R@-{KEyT!XqOF@T6&a2k<7{<2i-yfWZE-MFv*v-e zBWy-Xy#ag9@it?gY}IfwSTJs0acTl%?7;sp3gaYk!+TO%@Ul`%XQZ zMegz2kz3ig9!z$S-!K?AswZovJN!07$)-FYP8RUHynLvg+q7=jMsqCHQ&uSTHV;(V z&jFnb9&SnA`yhhj5T~e`IYZ$WMR0g!$>6==?f`pBK$ajwmAZZ)F`D|qORKZp_Y=DK z!|+n>R=|)$i~LVIiWLvINr<)u`l_ifB3|ZW60Fd0DbAq{0)cHyGawzpXrSQ8;&oX8 z@j6~jq2Dim`4xwN2Bi^p26Wcc2&CvJ&>P!J?Lq6;))(CfpPNbs){Y{8U9W}LYDe!# zBGT#vm3E4mHUJD-*?_&YXLjC}3NLg6kn`R-7<3#^B&g18F~iX*x}z3T0w}*)>c9^* z+!)AL1^jA_W5~=9K%N0~Z0LZ!r-h|;>{tOp%a3H5jw2ifTDdm2H=@0f>c<$hx}_by z7LOPB90#EWE?`C)8g4@WKbZ$044{ez0erT5B2`XG*Bzj4vBfuJr=mDO=Tf!02z+%X z)Cin7!PtyXg(Ppc{A_GMlmgFCnk$c_lxo<*p6rYAWU+;%T)Vn?b$xmK`udsdUawbc zlX^h?pN5^+PfZr^ER2a}o1>AYP0;|>nC%^Uzf$1Whs6RTd%QJ<1X+kju+zvewtYrY&_khD*k2z=fv7k*;cVjI| zWedld0(7D`8}Uf9>6wW|KQ>@cwx}2ng6O^uI(St=nvntzU%82!9<{;HF;sQ~xXx{4 zG5To;zvFBWVUMGT4|$~*f8FW)6{sT~XZE1%dQ0+~UHKwS(HVof_0frv!gRDV`%^CM z=+ScwzX^dR$N=T`;;#oCFH3^Of}`>-^JtFo4ipgB8x>KiI=B%NYzVUNqd!08pLjPu z9iR8vN8bBE<_VAuk31PJPOzy_Fb5602(2Uvwb6CHV_38>$^-L*P(&fj2ILv}~hWKh*8``oU${f~VGAfBf)yeEG~AZ+jb&2Frz>#Xy$9D6IoG zt+#BYjp)fHfh`bJ@)!>mlBP}zT$0s+nPe3>iTC}=?;6)OX}UH6{jIgNpiHtR>syi) z^vpmb3O(WDM!|At7gxvc;o@0hU>^l3qB57_NmTSR2hS4~R0FO6$N+BNC%SO^-sd2> z%cMgy*?u1oqw!2(2nxB`(M3nBOdkvnPb$A&8!WrT72+U;noe zWJg|8Y1Wgebq4(dZ$+VpYQUW#J|O7399Bp%ikO9h$OWgNFwYga__u*XO)@;A(iDc# zlPe*Q8X9l)b38uP(0_dbq{VGUviA{iFsWsB`_EXQ&mHhvR%yCF^o(qK?*C%j^9GCZ zkMb5YwQ{Dp+N2=KOq%@+gCQ@x=VO$)+LHzoq9eP2*KSG8N#~Go7SqbmHGs(wR0tFx zn7pBq@`y5o2~#1ZGa&F`h(5x2rBP^`PFiSY8^+fv^x zu8m?aYt!jF>)J`IYf!V8%DR~}%b+swx(_P5v#Q;lRn3)I?9OT|Mud|!c<3HHng159eUs|xh{BgI^8aUEJWc2bt3l9|m@xj8?{QYnrq!|M}6 zq%|WBqV`Kf-_fYBmm7)mI1ir6BS2XvLZ6~=DXvu+%MWFiAarrc%Fepq5kp3DPC+6f z)R<5ffD?3p3;W)fxEstRg3jz5rfc(M^7LoG!PSbb@XjF__6`Q>e z)@BpE_!;mAwRk}NcO8ZjRXlQEF%s|}30@3gSk&KK?Zt1T`mDcy(Qiz(qyx^LO@*-H?PbuCN z-S#j-8&Huh!KnZiez8CaL2@G$2u$JBoP&oseTk9DqLw3pS1x?SwzsDp3w^v*aofxU)Ep6FAGR%$|l`fR4?IVv8sn~RYFH=IaiyHw{4`w zE;cgKG%WyEjwST#0krSh9TU9TOBP>kwb3+13%csPnk{g4^@=@wTZK{T_UL8vBFb}b zo>%TxUP!;8k#VXs5YcO)g?F}%@uem|_-ZpXFS#O8uM+u@lQv;BS{ab0qJXue6DGZ` z{0ysw%aHdDTv}m=0@zUWQhp=D3MV&!X6{l9b9)6w$7yp#)x%Aau9N@w|G^M{(sRJg z98bvv;kL+TNwBI;8>rz*8!NGiy;I0m?2M}OwI-#2$L^J>x>C7HeuI*N97)*&WynP2 zz$S4{%~_~{me?0Lk6iG3r3fBI0I>H zoxMeB;_xvCihioP%395}A-V=Zi{xZ&RIeeb!VP_n8s|<0JEz~`Ffgrn3=pF3YD?Kh ze}QSmVPKyvR*u;h9&pn-w}GkKZ(v$-93a@X^Bb6!9R~o2?KsS&vj(mSnVU6~h9hf%Q z(WYfX+U{;?!u94f*?TLtg{N;I*wk%KYs{Md3X?&}fZA2gxuEp^n-QZLQxJQs=uz}v zc<}>fJ9+ql=P ztINAv;wYw0!#Z_#TueA|7parYH?U55nxFy$Rg(pRf^>O)J-)nncXRn^T)CXSV<&ts zi#@*1bsp4f%D#$r*o!z>`JqgKtcxQc1B1AeqW(l1WHGHJw-+9wFJ`&qfdnP-Pta$? zYoA4P@#uZ;oyOrDRVv=+Ai5J_N2^-32&@AhfP^M~7%XX67-P(#xtNds=0W76B6=w> zS8yayPS-Snfy;bKSBJ+Lr^~r6*%B6c|Ai6fNGgug%(RQmZZRr$9whBGt-Z8`kPZfDJaO1^q^i_s+|gFZXv8r&rp0%hYC_ z0pF)a?hI3~mCp5H{1X?DtP6)-|k@)+|+12Fu;#B+c7>C;0t`_4v z9`1Xs7E%s$UpgB)%hOTneGI5v>T8RuW?M`@Yc&pqA2s_mVT-!O=@l_!YOs0-r)}m% zBe=HkyltSm^hbrZmb;V2togioO3>WE?9+b{f2k!0c|b)0bPO&(F06fWU0 zzEvf1u49R`umP1u<2)3JZc&8f^Z0J-FTJxi=IH#evsl%l@o>c^#w0spax$+S%h|Jd`VhR7-cr-cH$fm(60eWBQM+ z1_?=an1vw7Q+a{7IX2(6!ksJx9Pp!k~&zo4ZXT*2K&hbi_@MGSd`QQ zm5!C_JjDnUPNpUXyB@57E48Zfc+%6-Yg1N83B&AZy+u3(w6f4&y7mwBAQgwiD*=KS zuxnCx1ud#H!&F`27c5KoQ`MJx_7v4x?4KeBP3aL$!FNpr1wW;g8o!b=#8XryCq*ER z@p@RmHmiqi=Aa!4wpoewKD#h1nfSV%?=}=2G)HUsX4U1(>YYonk`8^xSiWov>sBdB z68n@K9iD8z0i#8+hFMwL#W@^EhI&?+VbhL+fNw?Plh`pJb z%Cvqe)6})rLvj_smraX}@0&b}hr7eQmjzOk)_m$XQ`35duOX#?7Ez#EasAFugPYEBVC7-OR10SdLo=sC*rUuYV2x|?l_-Z>`oR)BL zS~a-N8ob3y?qU)?J}#Jk)z%fhepYgKvN1h&`j1MsJ({povp|p4hr5DUp6nivzobeun^;~eX2nxv`!SP7FwMzReTmt?`%;8aFJvah5Z|dmoMO=rUh7i$STwoi zwU=TIj3ZVR!YTvgUCU{vRB}e|&*PKvr4=ATepxBu z4|LdcL=`p5ie(T%1?C-`>(!;fps|BhDQv3LV9qindApolpwWXL`fmYbOBa=T!j{S?P!6WAymFVm#5h=qog^u%?-P%PY8znZiYY)w5XkRfxC% zK^nswGt5sL0voJ`EDXOaGT+Au8S_`N9afFcGO9)fEvPmiKIXcqOK}?)euCc>!aJFZ zGC`#P5r==x<76I@8Xp9M71@6h(m=N_Fn$Tfx{N6=T)CF?A&mYWyKkeZv*x%E*y6mgwZUOud>8~|<1ShW?#cc>M7jYdKpao47xR+uuC zxgvrq6+XaCVXo;;-?6YCZ{sYM#R7p0w$G^=ELQtz)SBIly}kYc#^KHc@Kk_1pMbxS zHSgQ4kcvdj;Z~b5o_W)!hrXd)(?GZUB7s`I#=tBl_I3g~v?t5RK#cdJpSp%>q~~?B zT4FKOCuS(Q!0P3P>*MRQ4;Qq5AEgO7Un#ck7?+oB^qv>2iVGLki=LwW^jFGHiodu} zM5m}-X(|ZkKE@DXgilxiwZ5Hf?P??2Iu6$h&CWraEwhABL&sSAyT({3ozF&3@d zsZi|SPVx;db=1T$5o@t?%YUvxnGVoBZd^J4v7}TXUgw3mxur1B*Owozt|lKZKb+jy zi$^PLFP>ix2W9wcTbYaHQ$lUGTuW~q3uT=eY_d@^Q_1EiJ65+`omE#@yw%5?L=tD4 zya%|IJsDDH+vp2THoFeg?F06%aTuU3{$o=Q%SnLS=&0aIhN2ds z!sqEu1uzAtxAlFL)Z9i|_DQz8iW2ul)EjC-mTb&dl~fZ}Dc$t0J<2v2tCGzrtF)pz zacdKj?Q>QocG4=|giR@{l5)x_rQt57+mx~@`Ik~wC3QKglDeE#Agl{4mGs~Py9KN+ zvSz>%$w0~8&q`k+IVRt#A&s>P8e)cKj_uwbsCU~s4y*J{)Tw&rllm|*e^w20a@ym| zJ6E;H@0U?k>Qd`7I3?v|T8U*pH)lYrQAJQ{??~b#b3Y$Sjyh6hyEwTs8+hAA3TF@l zy#LDmdBQ%8Xg3Cy3`@^b83$|39z8lNA`3^*{DI0Ss)W1G;4|Gza_R% z;t~pp~aNF zsHSx@d77kZfJtidx)GqY4GlX?g0XfR`50^V(0869|nd_!nx6QnaWzbs1Mo{8-Ch=zpZL1fIP0^L0jrY9o%oG4hj>Z;e#; z)VZ{CbhJz)1~;K#XCPGYQ?XETnIgxj)N5m7?+q%r6Ggw$EdmVFN`xUB_4djeQ(b)> zAz2e;_&LVgy6|@=69LzGOlj%*-i3cFP{f2=KlM>tvlMVmkek#GlrKdqr&P9bAn8k@ z?6@?i$FIicHy5W~yHE^ytrQqh*lSgr3^paC`$K1!3#goWk8BvOvhq(4q$P_eRB=i+ zRQ8B((JgW+a8K)lAFb5!_7c^EJ{lR~?e^ai_{gCmTSPsk<%76q?YBLnv>f#K?GcWt zJH#=qiEy-X_?t~j(T(Z1#3!be_(VqY7g`iaH%2I?%SNb%HS9AiD_MYV(N}h zOdauw>2~pnX(c|9Hny0dj!=Br_2lf`5sGO;gktKBP)zG06g6!(QTyi&) zW3-+O-G0I9T-FDNrsd$!(aw$m?B?iDIUh6Eqa7SdNrCG4_0$y@nl=Q6rscrU5vdsq z>`9vfL({DTL(_6#XzB_KO`D=H(@GTPXis6fdi%{$m}w~rQ{lJd3RL*%$@u2_{0wb$ zG8lkDPE~58f=)0~#mJ@&cPf;VDk*yploSbzn6h{9i-GqixcAWq8>oZ)Zn-Y183b{R zHc0eupfpPb=Q8LI_F>xp+}ZQ~)TKKwBU2C7Up%9rY!HgWEDDrgM{0+ax_rC#WlKAq z6vuq>>JN)d7np)`R0Nx*>c^cN8 zM7N4NjdM-3DNWsVZ`FsJs`$u58M~yDTgaHs4*Gma7E|p)kl2AsDaS1pWpkwa+rS80 zO8?VwOKFqcKCMPBjb0cq*2gZT|F+;ITsPO=G(<1SV~BzloI>eL!~yHUEiyk^hJe5C zV&nz%)xA|vPg;rU;bqw=P7@xJL@XM~A-s33?ZLoo@UIa??73BRdbxE$%^L%g(rRGR z=ztQcQ&fGrIWQ@$L?)%K$fRN&YFic{Z;VY!tC~>QGYPgq4$84fX;W-cx^-;QZf%F= zz@xN2@F?Bh!6>bG80`%AT%vjE@`p?ty^B(}cTrmJUX)gKM|Pizhf&(>VwBeT7^N;J zBPj@N>tmEwoQxQ6s%y8Mmr>f}W>g3mDqL~O&blbpurng^9wy_$K9uPC6M0v&+a$=c zLUm0}yv%LPZb58V!l|5#lEUH?`I#;PNFs;dJj z1!WLeE5{~uXgn#B(CXHKQx5-DZcoJB^?p4^YL(~(w6ZJ#+Nt)`CQT{P+Tb{0fx z=NM^N$p6!gCPdl9aU?6lY-2-|O&g4e!~VWLep9yWJ%<=-)u@$D_ONAS_2LWai+l-NG}~5w7EGeLKm`OfN9$%3?0J(V(5j^9#YNBz)EX!6y>r=+I#EZoy1cN5_ z9Cr__?77^c!PqG=(1HfLxORt@%yE#QedQWUr`vdGP^(gws$J)8Q|qNcL%59_a`Fy{ zxl6NggN+IpHY?KGuJ`fsgExLPz8K5&!IT+TmP7ILMHq_|U1Y1)j!QIEi0H{{vxwU{ zlvmq7U+P5ST`93EOhT2{Ng@KwMNB#`t_tHB)kAj*Xq2MnO)ckYu#x-c#*apL<#ATm=Tjl_b@Oz~r4YO$u8UyK_|?C=?(ZO;l@8E_?J@3I9h#JvVvu$uBfe&;9=BU}tz>S7lkd zNWaSokj$skTK|=4eM)?WKbdZk@4W0=mrSdIrmC2kOr6AFEUr%_&oItPQswIp zMkuW`-r9tF7CRTpuZ;%vtF;66?S=AJTP)Pf79stWbDfHUpuW3iz&hTl0%QH&?pR5G z<{L61&7=Aq*x6;@348JvA1>b?pP&6|Z^Ak*`9wH3Azo~%J6D0`g@%T~X^;IEwJ=A7A=(k<@pYO$u5T&$^*DQe~rm`iu|k5pmKby=5gYNIC1{UnDklXH^{ z1i(~rzgQzcfGZ?CV4*a0ys^0QdbC%KkO%R6OYk%z$ZCEj1 zGITi{tjgKtsorzGb=X^!*JXGtU&oR2rutVfbf|VV@@3(@LSMR>OdiR@PzOR8f zzi;*s%&RBqrA7|R0uSqmGcXrYJLt52X)#py^-ca_CC*J(+cOVU{FPUyHJ-sey#8=` zF}{38U8~1yd3*>dPx&01P`=VDq()R*z|X@a5oiOZe*gmOZnPpiD$shpDO z?;LqcOw$7YUQ^m#k@USW#i$n_75PGIMk%@K>G=HkKpseM^x=hN5L`(pS zA~JjHimS-A=bY(4jfgf!ppEQS2s@g_$dt$e!T!oaZ+1#_Iq7Y%$1rdr`L6Q72af9OeCtddJfRFnfoj0N+?aO@ew`M?x2Nko_Jf zp=A{eylZmwAUN%ek63&V*TbW6H?ImKh^)j^^4| z?R53Uw)ldCT$SE?qrw`VS1LovC#4=Lr9P7=59Ypt{J%5~1z{}^A|p;(u%ua7TAH7D&abd3D0bkyudzmysk z__C@E0fbks<0gSzZ3+OSX_(8s3TvnVt!c|{Vo-y)o*cxk6DI*|v>*U@tFUy`V~2+^ zY&oW)7k3%@j{R(Xpx(QX*7?qkeL~-7` zi^+G1?4H^vszb$u3=r_HeGy_k4;9=*C&FY(l-+U>;ZDIi7Dwq7GDK)yPyZcZt)c<*+qS-5>_tZpT99le_-GUJsN7At|VW_6R2)L3Zy zXfSrYE5y#S50H*GC=Juwv!u^A$LHtcL&K>?<*~gB#cGvtG$mbcD=zyjq7*9y{_N>k zOJuOL*f^sEWLChgydkGuD+n2ck?aY@Iw~u!v(*)snc*QTzcZj)a)@FrdAS-%>GlqG zNMcMo9N5EEN3ppqPe;K#W|3+3&gpv6LAIr$bR!xXFF2BuIo+O&!#YQtZwhd?U$i&S%k+yB?eULTLIu928n zg=Woc1)68IPCq3=MECN=QXn%(2@!N<$sW-9r!c@$BzUYsKhM02k5^t47m<5QKam*^ zsPyEmPiGi&wDf>lz-Vw{L#YVEOpwx1uW-JXi5Rq6rO3V@qxR5Sp7m8zET_V5gb^l$ z%veICrHM2RP}M4v$=_m?72U*#Kf}{v@qbheZM4`Q@%3AB1XVlqfi@nuPW7(I4rtsy zr9F3b)R1qWNYuh{7Wduch?_g85x>})7?rCSb9Waxvflhl)W}!_N3qdO^Dkg%B19IH zThXD$70`PRPn!u5H7Ft6zQt&8iw#sr&d^b-xQPZy#B-7$)w4u_wmV4GQ&bAqD*z}R zqKzl0$`aOeN;9(d5Wq-I&?13!1LzQ?XD!l=qi}l77`4QsZZo zZ+9-!5NP%Z>lnFypF?oi5cpDHjAj=EB-Y!I2V=^;YDWx(f~Prb~~ zmmyE}qW>{_(Sn0>7j?CYWVI)4s#?gN#Sw>jY_GkM$Uy% zU!etct`Lk=v}jr*Zuq}+jTCIw8p#Bpm~5bdvg7+w-p_%>Nl=4Ey1rW0Dz4#`WBDmJ zBEH&mkz<=JZo6V(a7m0#25F|xRr8O+rMKB>3`gL!h~Igqq0GjJv4t|l z4vQ36_`TOUBY_?m?+n%%HRPNGC+T~qoWaIdfif8mhmoWPgkOP>uT&%|!3$6zz30Do zDa!Q(F3Iuvt1*|F$Iw=~gHA6$e7=4=c{@HkeS1x4INUG%x3I1X&y6gp@aj)o87Z1m z5=mSvIl^B)U{0|k@9(^#sy5!CXMLf|{&~EVUi*Rb=Bw-c*Fb9LS8y@{^2UQ-rX;3| zl#-0Y3Km$61>SNnO3DB{K*PU9no!n}>5iyaH|s41{ha}+BTyPz*@ol=fkb5{X$6X^ zMGt<$wNceV6xG%_n|;DVsPRVq-A9k=$X@EYs>fkA#Z@;97RmFsXq_AFc}of$gujP_ z-41PA>T8YzxsV60@`Ag$TA?NXl`6N%qA1?@08=V8LdW7Z!b=#$LN5NI#(F<{@81G7 zmNhf~>7C#N383V!?a}icH0FhAY(9rp)cUXOo#&$>mlSH6tc9aRlPm{ik=g(2u)ilW zwCwi1Kj-M8{|YPI2yU0gAiJvZP^6{oe14?s(PF$97Tv^34Yybc00?uB$J2ErV$2`t zonnZckigmc_7eJe-+%7`T-^erEYA~Me9O9H+Jh2pj9TG_;^BN%M4cBKwool_%X^=x zl+IM#`72rGNFx$!0F+nC0$ocvTK%C|7+GO>;RBNuERprKLfCN)?tr#L(&Yh8dka`Z zM1&TZ4*XlSEMzPp@0b>u%l5kF3&V6F>0mV23-vKbz3*`UUH3kcl zroi>X>b!@=qBBPrZWnANeBnGpRL+5Q5v%Gc8}?s5;!&f5r#(8p^CBzZXHrQdf|ih@ z|JAw8m41TVXVfxC5mNf8m#LH8h~>;f%By88h=t2&;zNxr$N_Q|*JtB5SW; zBq61{qN2)EK@4+%QIO^q6R=9(+9589Y?p*|vQU*-02;$OqiTzISY30iQh^6X}p*CS#@=a5|_UiLpEyA9T`VSwaM` zY1A<-zN~2TQFSI;^oB5p4lkAl<8=Ife02rFLn_M_U7M zh&U?`E?eNDQ*V*PD=)(`(ZELnqQm2WStc$$#GyWzb7c`c0)MuMF-_t^v~alg&70?E zZ;r3L@x|NWXgKQ9?BO6J>su7QzhZ?U?>O?ac=feI0owxDH$-c;#o%2<*DBdfzN0G6 zv;;^vaUPUO?LH3C(SR1KOM9ggxm{u6?-X-LE}5uOW|8mouJqohI~cDrutwhggesC~Q2@9wY;btVreD`RkXq(YaHOv>%zlGS%?QJMzfT+qfn<>uDCi$$^~ zVm8+pL|MaW!{VvQZ+`eRzPvm;8DA9~Bk9@UI`nh&H9@*AYk%W6ur^;5nZ`A`5C}W) zz$8fT@+D7AYkqhe=#q#$$&ecaL#`#}TtUW+CZWzvkI8`|B|51j(s>DirV4}p=!Lg3 z&OV_xUg$s8oO?|!HXVOj2squ|->Z~hAYHVJ(kb-xAL&)0K56YY+L6UqYzIMH@u+GZ~55mNghYNt7r)@J(G+1B{N~4AC zU<#$+q*)9ycVq@L0UNWV%q7_RH|YJPct;W51~lQOKX`4~w#QF8SPa~P zFQ7zPm_bW6g&Qf#Iwd&2qr|sw3JI07+QJTaw<17IY>U0!(-PZ4*S?mris6V;=d|1; zMswm{Hb4UZ1^=Y7Y#s_Ol36Q!AiCx8Sn+_OD$Bzv%?I6LcRprU2a*_5p3r2jMRSs@ z15qCBI8P%VSO6-_O0bz3;O|8;$2*OdRaX{{BcQ#7U{DNJvZV4=#q>+cD{;z7h`!ww zu%8eBwiC9boxs=U?fXs#x6Q)ABa(4J08a%SjA7UAV3@?P&0+0l?k!bRjI5C&Asn+g z3Fc`V(5N6BWM08A=y)D1fJ5)S1B7osyW-|M&wv2F$=RL&=P7-Db(aL|4EYF6D)^ND z{U60KOYyi+;tiBa{h8piH323}*vE@X4`xXlvycW6YTQsu*y`Y#JUhGicys;CG2OI} zvB)@MIJKnrLeT3czw+;Z9uJ2go`qq-F6m?1H zX|Fm(ws57dKx>{>6X{1-Ex7dfItZDY-s@VhqecQarsR$=PvK!IHT1eRt#>CAZYBP_ zqrQY^7=~1zxJj{=ucDGIwD15Kv3d7tj(My@;V+o*wG%;)j;O7969aARohDdvq00(+ zxxPrKif0U(@Dl{K8O5;;BtE&)ASnNKEYq?;Yr9g=<(@MHos zWFa*NAAsK8v0@~X^0{KMya$1L=sDvdd4Z6QBfeGpjl>e3^)+rNqd2TjKq7>`(Tf|E zS&b5bE3PQr*|?@i?}R@jP99c5c2%#&VO-pd376z(R96X8{XCG1D9yvLz;Y=i;4kDj z7hUh1xCt7AdUx>`@+$O8!H#Ktjx4s+c!LdPVw1M%2+mCD(X$UX*T{@^6_8izY90fmJl%g4*2R*!1UqaO2+F9l80bzS(s$%0@^Yo2mxPSZ}b0V2WWZfx-?ENYzK`hIlDcOdA1<3(i zC5Tp*B(>M17_7XACFreQui`iagyZYW?xq+dtNoiW_A}^UaaF(RwqLreheS6U_@gwU z!98pR&3bG*7&TT?9#+O9o@JY{sG%BXr$5{s z?!7F-hRljJ)>-6&N~WsDU?2ETldq+67{&glNSG<6AAN3?B`?t)Md7QtELk65_Eb(c zKeFqL+1Kj&37Ku61W4iz=`+ehNk06->V=pDqLO04peLqcA+Faf{6ozZyH}l!m}Hzq zix?rtHukJvl>Lk-QcsA&4GsIC&k+rzJ&R_(D|eawHEiSfTCeoEg4h+gn&6lH&BCtB z$rQZTSXu?C$q_L@sbp4&7c)p7kt`y2$ZuYZJ_)|5ME_|SD9J0J38laBzl!&XQYp9rT}y1?E)P)aXDCs)>MN-GIXw;KGH$3Sp~tAi=n$UWs!Sl9 zP+OKKMXAX9$t$iG#l!mBr_=JC)I$$cpCVAE(ek%TqcvNt8zWEPeQ9?`CLs9Qnam4c zSnjFkmEaQ61&d=fSI-+tM(BaX+%DOq7*Bx--ft<^ z8}0s>Iqlc3M?Ruhvk;XRKp&7zQ2wb&oK+Fy5+uNQB{MetNR1qe*Bfg5pcYjGnS2_* zzW#9etad`!&zumE(uTU!S-CDX<7sb1X~?oob*Z!M>r#IzxT(xlk@|iWM9p#W*un*? zS!6jYSEQy+<-qvnRz+O=J;+L%VDcYbFqQ5;HFIiShNkYl4To7~bwi@|^ES=LN{f#8 zRHY6ya9ukq=Yeq|fY(GNm8y?D+l=n%%!TbmCU9&}OP5_(p3cfh9@eH`31im=?Gb{9 zabv!tFy1cT(I;E#2pzS+>fuYv0Pegf08Gmex(c@_@hJ_<<;ucR-7p7a=k2uA3U{L% zt7v`4Xs~1A$nn*6@9g4a{EOD1lA3MJdVA7ZN8)zHVd@F1$FsyoU1`ft(InLZPFV+o zp&#SpkJ3`krV7a{C#a-kJ%K-6ie(aD(lBCoS3w5i{+gZmGtEP@?i4S%)`JMq`I>aS zm;4PyS!W|j{CYoPfG8%nzZ~@SY^LT zqm|PJ(lI!!qc%UH=I zKmx4hC0&u!S&%LRrCL^dZ79=lPg#-+AH0%hT3Z3xS3Y4hVzX12!&7bQaJy=BKQn6fa z9BI*GX`|1X5eR#er0WFvQ}pO4LPB~nE=Gl$AulU4&dlM?gbpd^Q6^7LwIJp>(Tk^^ z0a)v=ZM}H?L>}P@dVeQo5Bp8IpQ^j8o6CB|vXyBUFMEyDy4)?vV0|%>{!1O)ey2~K zY_N@2Ug4AHIGd^)IiE>`#}{K#_ItxZfsNFW(HRSojX{s2EXBMOuy#|-6icoUhAlzn zEcD5*m?F^vKs%Kv2#aMHO8ly~Tm_iafCf=c?vOgD{ZwS~DstFDbuZJLtrQ|?^^hKk zmZ1$*GVo40BNjBl=?eJYa|Cp{_5BAN`>xi32xaQ=#2Y`Z<0Lyo_iB(vS8@16B`&Yf z#ifT-?LXko2QY*&Cbv8CF44Wb%fB(MLAfCHDJJMr1Q=Pbty}Wp*5q^wRQo>H#k*5=_j= zPVvOHUtf;LllPeE;q3Sv-3{BV_mfwCa)&q5`^lR)3^0(TQ(Q|EIP;S@natqQ&BSEo zE2oedR_awav!e9L1)-LocF}ok>LB!W12~91yzB$PS>&mh5EOX2@LvlXp_MUUoPYNWS^guvoa6Mr%7+98APBC!ZU6I2C?$ZVJ<9IW<%y2q>FNXNtyF5ODH6^zT z3fPy~dr9wO`N};iZS@vz4)}bP-avejWzj_B2dw3Wg+A z4wV4cageHdH}_3pXn~2vbR@1M2Vnr%0W3kV^3D;qj`u`8xwtpOqd#V5d6i+h97e%| zy#x6$&X}Xy@;8k@Uflo?6trH=YNXW?|StQM<;`rJa#8AMrd)RH(i>C6C5LNEm7%Jup#$ zRlF0$IO`Au@Ye*8{47^+=eQSIZBm!hWsSl0fpXg7GyUe(FFpC~B`ODbzot266UcBa zX&I#dj_}UocwOwrvWPg&BOr%9m5|C*!08Dbq@e8Ew_=}cI%@h{;^ndu{dF9EUB*#b zbHzyUOn%Nq9iVTU87B@%=P$(Mp7LZm3+hs_Y-(=u}W z4-+{y#_MsQHmxs?g%Ds*#Gz7O49hc-Si$>S2-tj2HUa)7;&utsWL|D$&)}>vj#Y$H z)v6y-eCQ-22HJir#Q^SVb0NT!pUW5T?D$O6A?uMj*)0M}Lr?XXy;Zn&z7CDH( z6?xKS5(b$%hQ~QhrsxLFsfzo^PipR$jY9IUrFi84yk)}AG3)4tj@dv$%qoCP$1U6! z==glmm6jX{;Gy?Brbhf-da7px5YDs656dmPGB}f%1Im}o;m<_rf6d7>Mdq;INk{Oz z47^;*G>b*NdQpJBb(JOgEW5lo#^`F&`LUZBk_5Oz$x9wxz2_~RtdG-p{r1Hw;d+M) z(N2?@rMp}b&&YZJff_*AxcOu**6A|w*SE;^wmX~k5EIPRprZXsPR7a17#YW#FFP_E z5UZR5b2TA-CdEvp6m*9U+Y*AK`Eo1$27X5CT@cS(C2a@F=YEz1j}t06i$P(~#e8IG z4on5fmZ5oS?fWFb%;819#IaifA}8L&GjybOM;=EN9f$|3cqxx4T?g`SE3Uw}0F<6L zB1CYAF1Gwb%3YvhqnAj=M&!Ob+_k7#=yUq^jYAE2sNn$)va?$n%9wRoz?s3El6I$| zg{~?W8g}kiJUheuDGOVyjZ_H(fA*aFh~M1J)<;@!vJQ_VhaR_-qQJ&>F3Dj9gn{E@%C#P-1R7NPx>EFeG(%@mvlaRWakM)wv^ds zT#UyFN+s#h&d6Y*-^J0rcog1S_5??Of4_PC%j752YX1B?lJQXLfZhYcPr5N7bLK_f z8{ED^D|8{{$d5M{@2)8#H58PZ5}EJcjSGkZ1X%m=Aqkd2~KIFoTYng0>#S+;vWZY-#HTtlPuilpPO^|j0 z`^SufO8#?*W78GAWhOATS-k)0-2>NFs9aHuSM56>;_aG2lqy3AeW3ARUe1PpwOJE`1z0!@;* z2dacf`H!;OJ{9ST(smIC_Hc^@>22Kjxj4$i3(7Hmvd7rR*g5w?25B0HaGiw#95q@{ zkZ42s`-q3z=6YjlVxfzeF9kiKKj2Yko2~rqzPdnaBkoC%VbuH51eF^Vwnc~Lt>^j; z!oM@Q1zPnVN?W(1x6XxNg_9>4nq-z?mGd4=Mc$aaxcsTSVI}RO91MzWes_*ak4rP9 zKJr>*!crcbWQs##&B|L$=ZXMFeQ!+8o4_a`Yld7VT>L58ujYP{q!Va7rHLi`>Mby{ zKpNn6Rv1{-DiI1Y(wn9tQWU$iPy<^FRoVA4*FpoqD&Jd5G=BV?^M+Z+1H?IVc+PNT zcsi+%F62jDTaAjzh1|W0Rxa9J9aq&Z(v^lBOMf0`?S(u!gD=HZpwZ1w1F{?gB5-S( zbEg6iH~i@cp?JA95V!=h;u;i=j!`T?U_$#Ne;+_B!Gh{%`h2xBoh9o3v)qJ3_u17u z#LcbKDBt~zyH|G$*WeA&r#_WBl_=!3>35HRW8yR(UgyctGtL(}`LFvx`+V!P z`bdNZm6KYfKM27OIE1vVeeW3qNr2&fVfK{ID#I5R(HbUPBKyW$X!T83RsHu?)tgFr zI!H^M(bqnZxG2x%OpT+N0cubDJSArKGNr4H@U%vh`Nv3?Px?Aj&@LnU|F%kHw~1L|t_$Y?I#*eiuualezV=eyl93@cuS?CV&H?T-`#=>+K&qMd*%upu zB?Q4?2oS@>o>9zkWy11Rp^0u=76^QmB|u zq+NYbVF__msT8a_2TDklI(T6tg)MzM#Wt9WQv`o={CaH3T`(LGCChpND?-$%A@vXD zPek-T0-n|Z{CC#X$Mss2{1nM#>Evi7i4*y!1mP3|-<+eV9E3n`uv8h{!7HBIVT<#0jo*Xu-U`S(5#6=h?lEck< z8|0d(U(<>@_X<@<=n@g!0KjSh zhd)^Z85ZsOITrwG76T-H5WVrI(rU_|N*~ammZZ;3X7Q00FDSDX?=8BT(EF(TzBlYN zIRlcx7lV~VVkUyFtWoKnC3LA{MgF&6F+%6s75i^57&*m;{!-haFoZtfTeBynaCJ2b zQX-(IHQnP{5o?cUsX7!NaP#_?o~6_U**yy5)Ny$xxLrWGL~%9fB~>OPllD{4+VG1L z7jUeN8-5#yBAqONAL4V$+I;K^0tp7E91W`K@;ZECM&GuVxMhp)3UQGg9#lHmsX(`H zp_!j6^vMW&1TZ?LnHR#{XL7aj|oPkXJ5M>rGhMej-z~4{KBl9#jmRjD@^O( zJQ#(Vi^O=^F+Pt*aQ*oYYt9#qm8v2U!auSTrE@g&Rh$riA^?^)FboTQ! zE?R+Ed@-8t$b?oo_X#mSG?!WSl*uzyu3j!wL;v`XM>0<)#do9>Ir)&?9$H&q+@CyP zxf-l7xJnwX8AXxKsK`bf9jQQJk{$8-;NC^SSgaRomGB{d3hBeK@^~IRcHz&6(uQNw zGCM^Dz+ImGh-ZJG@{A_zK6F^Zw#w9xlgt>b)vB3l8yiw^T~Xql9le1Y=)dZF&t750 z*=K$4Tu_7t?=JP|d#xH~pml}o;2(NF!RS9veo~o!f1X_ZL=*kF)v?#-o?F-pRGyO> z#+cZUS81qlq;5ygeO%r-C1s*9jDl&$c=TzS6HGWO{~Ez2_dDfnT>G;C)4y3{LG9#} zGK-sg*jG5*msmV1vh606_+jF&Fbzd{#PZZB<53$e)iKB)+v_X;L7#q{igZ9@`OUf=o&)*?$(K(RViqeHsS-nE92a86T%k@RnXTQ(d)f)%V6NvAu)8g<+AZkPaS~AR-ZY;2p-iSrfuhLT zhB?(($yXUbVb|B^|k&)7PHY~!Q%`|Ju z5+u(%FNMmQj@Ox(_kbu&gAlzA8gLNdq3Xn$fzP|WeN9dy=_yDiWapaBIsfJ+LU%%@ zT$94@4IEUI(w5gQD=%VgX&6~ni&ptQ5TSP}d<@QQk8zzl^$Vc_$cqSG!89jUQa*JAWdmA5C~K`V~H55O2f5Xe&hhsJj*bly}(+YY^}rU;?5ML}TAL!keZ9>hLqVQod z1zyW~FiwQaf-rM0@4H~VE-z|%P3?^O#0@NxoIFUH`GGgos+J>YWc7b7%;f6!=i$8e zvYhj#BY9*vbmk{ZxVsL-OiVJYMLLm2^tAa5(t`Gs+RK1^$C3ed>gI6983>S+k*tDA z>dbP@?6gBRZtn^6HNl^m6Y6~SxM8txUjKp__s~SP9n-};m+Znf(4BOLe+L(2kV2#M zmX&oulwuNqI?&i3w|OlQK1^FiCS~(n?Z{JgAP?~9pblUo6QJJj3aEMw9H4TL?PSKe zf#(}m6frP+Lhsdp#Yie?oWi@6#=LF$+bbf#)6De#P-y)TVM`~+Vc1_xAG@sYWB1c~ z5P+9E=9p*u@ zQEHbl)*q5NvQd`&?xp#Z>mQw*T~0n8U%!1;OVfZ5#56>Kz^RcoC~_P!J1@+JWu?3m zM#6)9p)ZK7F;%CODdklmC+`qM$`!MjQO|UHBqyc@9tyAUeo-TX)4-T%VcZE}YDc6@ zF-5)&;(IH3VodauYMS*r{5o0bbSsUksq?4!5v5f51~t`EOrwCS$;n&*(zg=5-V+j- zymm>kQ|7gnT&E606PCBx%3n|N`%Ra?ryNJO!mXIIf9U)xz)#G2?`H`Edqmqi zDAlZflC{nBfhA2DpqKnakJVlE+O8sHpw!H^)4tN=^|7eJx=ktM(mpFoJC@GY)cgXz z3QF4ar~osv==IUw@@URrJ~wx@@NL8q;4Lh%ml!0bqW%lWUtgn*)oodmMX$R*DCby zFt#-E7{8S1i%TBnh2(;o2en*le|85bIq2(UE`3-Rbq!agptt%;u*UrDw5jxoK1Z@r zwhuD^%uJ0cqj_gyLxNO0Nh+p6EXFpBv)@v*)`nO`&*m6BWULvM{CzWd{o&$z{LA&x zv*Yvg3A-J1J$m!;`1QNV*~R3;#khP!)c!{uFUFrIH=oXr|1!RmWQL!=`tZx-$MDOc4RQgTk=j(t}aF+eV&;ew1kI;R)z~Fj+&9=wQbS+ zQ8rJQ9;@0%HaWq@HX%v##Ugk#h{-wYRFP7c=~T~7#uwLTSGGPi!o)E}eOxKMX)ow% z-ccRSfc(H=DedSL$0YMTjkh+)X+*bF9uzwjX<*U;j6=_}l8%df098h{q7cF9cn!Z8MkA|KHx#_O^{&>+cunf8b>yh)N|@&ThZBa4zgv&Z_I!fo$&< z^)(2|q8+Ve$rYup?I8dCp0{};XDC`px(nR4KpTrQ!{N-CGw1a=s4XCXe<$rG+QcU# z#4%_8UvtV9K`Tir{dISmk7np;rTd4T;ZQC6-ey2nYYHIt5W7tZj#C? z-;;Xt7Nq2+qLy1jl$+LNKlx>-}x|o z$l&;74j$IIipJ+7q=JVT{zcFWiIpORpz?Lhpv1TkRH+6}6-@ z(UjP6ryvR;F~#?T^R|8^!73;S7J^@4oqDd#0aR9Ho^xjGX0%0+eoZby0iVeLILogp zS@S*p*_BUxO1>|(#Hwp^#G*q~+^nKYWp0x#pg!h0+5FL)qa*=gPo~{dRl8@??zyTR ze!yt;Y(9Z@YDU|XSZY}(xd-<3m?HoHX6wSgF!|Co8*`&4-RQ~N2-`esl*Crcvr{sw zmg2e+H!|$^-}OPTJ68sQ}(r^n2E$b69NO1@$YvaA)m1d)mdpv~CvpKJ;MkJ+BqsE*bzd5?@GZj$#+1uN|>Z)MMKbD-AmU z!sFp4XzoJX0nJPr%Pd{cry%8jY7YDF250B}p(zK8tT%hiu7OGWgPMpbS!i_}rAA;Z zu-JGxP`r|9{4ccEO)=3e7pdm~Q3OWg9RD9waATMwOf~xl&iaYtQY5B5C?*G>lAH6u z(;PY`R)gX_gPhyHMQIwa-cfNl&<-Mw9BGq%w=w!lagV~tzh?8{Hhw^$G6__%*) zmQ+%25IhL!(mrRR#tQ~A;XAS&nrR7hU2Rw=HN@qjtmSI>CAs;1^x;8#@ioI(Tv}0* zq`Po%a|Db^5~-vBYbD;9xYa77&GIyBF8YtW-mG<)m5CDe#>n+5%ienNx>Jhfa*E5@ zj&5)e6nHU=+6|{2-A8)b7@0ZfUUviwK^L31bR(I%LGDpy%VdnVv!t=(V_it4a2jlkw`_tjMdDf&?+kJrfI&sj_q?|GH#cMl(eavTAzv zcK^7a?jIlfdU~kRy5td*V#mt@_~dS8zYojle3itP5`TXcHS*medx9G?zMaBTj! z6<3+P8Bpl|KC+Y9L|?S7j5$X+K)t(1kn1_&N5d-Aysj z#B+JE5aeH{(#MOnqS?rs)RUH)Fm9E7-H0r0vbFp2^Ej~rG}r5FJi5Lsx6qHeGy}M2 zu8X-;H+XZ;d?Jj-(=@kMU~d;e8*n&xbW!ulX0u8GrozVpz4qjQH90V+`>d4e2y!|w zhh>hEeQ!geb081v-k&PiUy7Da64pE-B!t8LlM}8)Ljv0K8V&q+gkpmBsymF+nXuzK zpJtUZ6i6F0L3c#}10^m5zQGl2R8RZ5K~M;y@k$NE_8K)Ns3N<%0{rIEdlc78=#Jv# zM_7Z!ECX!?&xc0^u@8wS@7JB_;N<93P16n^p*fSOv5|LcO|Cc@TSqQu)qQFn4NeAU zKPZ)|%EUyGKzR)#6YF_!%~_DL#*zPzY)O76LV6I}rPT33UWeHuXn*ghcDrs+-3snw zRThA{GmQ1w`wrK4d}cqA_!&v>Vz@z)wajHAHwC=+rI&;{ zS!SBf*$PZ{w*XzgovlrIro84f*BHBR6gMT;>~ckjla5=Djg1i?{S%i%)-Hhx_Hegk zYR@H`g3T3_Hl?AOwlDR9==@F<8NI}o_F1eaQx%C&Q6q+k^3U52I9{P)*lnTliIhbs~iGF)K z>J@mMd0|+Kk)gDyQTHLF5%=KyOvOAgrKK}C%oXAPPYIY$PlPbG-b35!x)ot92sDc$AeF>6&=yg&<3|R2hX%t*eID203I7 z>zGZFPho&VoRNC^ChC25$bjAEhU~m(|F%cHKnxo#B za&(7HX=u+SvI!yfQ}44N#BMz4j36uPb2+_III{W|oWoTaf!wY2%_K1;d5!HjT2NF5t8&@7kw7BkfTdzCJLRS8ptk^pe zMNMkfyZ(Dvva|j7b_DCj6~j^lh2*GuIZ(^&4h6pDnlxp5K#UsPjXt8VC>dCk#>Lw8 ze1TD5p_HwzbM+}kQ%G535Hl*fO|{&*;e$w0oSx{=$G{CocB$~87mj##H%?m zHu)NS;af6~`%Mk{@yU*Zeh-UnJ|c|>)#h(F-{ zV(c)?4lR1V9N&yowiFkQ>E&_HXi%`QL+0mow!A?M8^jIb>?*kRIv++$cpU+dWNTn} zlzWZkT8i*rVNc5Q8m+Pl;Hjw~SejV9g|Xa8Egk=g+!)~yHuzfFQS;*D@bp{yTqLxB zmlT5|QcIy;Qg!%r*&Whj+eDam5&aPBz|U{H*c;;>dhN)9H#E_ah4n=Mcog;TBV{U9 znbae}Zk1^w{+i_~)@D~7bg-sMv0gBO}MlM|=WYtGKiz8Irma7$;%FJq#k^p`26 z`(Kf@!ojlJX#YRaxN=cr(l)bbbbB|AM#!byz^v(bB|ob0y7*Ig3j^sprMtr8_*q6RAdcuTXoqBd}8|u z?VjpN@HqpoJd8c$xWmKiekCR2A@gKc4Ts;C9C=}9o{8FUKh}n%w|?fi|97Cg>Sq6} zO_Ym?6c#Id(#t%xCgLii3aMMVX+0bBr7l^T(Z9tbyR|EBu^P=t^FtAj{=l6MskNT( z@yFm37Po@kL^h~RZ0ImG)Kn+29^0kp?%J~tRGprOj0~vy2~Z&13m~be9}&E*&RDNr zeTuOk0u#o&k<}Za-|E#9$IPflK}j=X7>KvJqnuA?E7Kpb(SU#m8+cd5fX}sqF8>IW z`p?mBba347^>#KfBG*r)114A1(USfmmLA31E#|u|QR{5tahnRWVMYV$BNR~t`gN4x zZ4=MP+3w44@Y6IIou!Zn2HHQET5wS(dcC>c3l-&CjLW`g7s|x5fW_^rm|f4ST2R?Q ziK&VMy)9kc+ zPMwGEf5QXTbphjA{3(htC#iqXA4UwL86}NL3j}b&F@?@l5EzdijlE6$CA?=V(U!~U zy-2j>E~=;$mz=9^RGC)%n@gr;hC+JWcs-p> z5O99&l$P3#S%(CR6Oghx@0RNakZk<%?gme$uAo)^nl4(;S8$gt?~1HgKO`+%^_1k&-4vKnd;VX(sv|)Qhs<7?Xd0KDSch?W0Hs^flu>WiD_ExDY&n8#Pi-m zsSCt{J<)S(hK(@#RU=Q)ubQ4U*taV1Whlo9xW0052SWTEaIwF_vSZ|5R}k+5nLfN> zjt{lQRa9?HUbqs@Dg;)*EA>=-!`D6H+Mx1f=iAn#wdq6|4#;98JXT@hMM(gO-+CQ~ zhbnTXP$|dEQ!3Pp(hBr><0(d5X&UpH$9R*AR$d7hy1vUOumFgp(Bf}{bGR7rmWY4I z5Pu8gYKCf#&oOSsi15AcYy9;9mV^HKOOT-A1DG~kOmESy#N)N~kH=J#UcF`05El={ z{`&pjhdaLoPB4$oM{sHD?)=-q8i+=I3w9qaK%qUFjh30bNPaqAj5A(|?>_rPaMHs0 zB>2qf6Gm>)zA2`VcPA+U9|i2>lR!*ewZ5TaOfpabms~cGce+ajWs(rj&Z*2RPzp^B zx`$J%uxa(&s^>>f*Hj01atVV4s#l_R4HpA2T{QNt=Tq_$ca~V&XNmO-Uqw`i+MCtI z1EWm-^pGX+wpIcX2yiHT@vG>jMK{qjpHha&H8nv{-<4FXm1Zwc<_a=$0mJm=RPz#S z!Fqa~^5}}xs^*r`R1@{*UKL^1LiwwwSu4r4L*SQTHolpj7$A@U4^{>YjE0Tki=W<) z9^xpWTp{4*QltJULXk1|Wu?>hd0|}xH-9BgC@w&mI&`rXHayyB?M0i$YB}$bZzx?_f4CAWY$IE>FypR$ zZ5!NIGNOH{{p^cPXSJ+mfdf`y`H-+VZNUn{|$E{9Q zVk{s`R1ySu%F?9*1Ck}hnM*iN`M{RPTD8G)^V6XJ^LK~ohx1gKDA;b3P&Gg+v-OA3 z`eD_fUw6@ShFV0M@}b+>Y0DM3C@UXPPRTDo;2+#C@7l?n+RB5MCfqObVqdmrXp*Ka zDvVjmoHlA=`}DiRrhdH4ZV~NMJ--;3N7t}$w+pz~teM2NwP&&%{Tep)0R1l3WDUfP z6*+yVYbMsStRivc9?z>KD_zeD4hmZG@E zEzjig3@vO(Auanz&&+J#kM;fBKOuO=kgDkuiK*Yaj}~GzOadlCHFMW67~=0!So*!@ z>Baf+;G{oHPY+VMfm@|-B2r9vXwl`xdEv2V)U=P#u}RN%x?Qo2X!A|MZlrQyTh*UI ztijokEl2<_vf!>e(G5W&32u>z^U+<~GWVmwaX%dac>{+0_K$|L{FNJU1lQ#+>l_oc z2|`FGC^kjFZt8L0pB@g52K`~PWwSJDbhaLsSl9uDy<*JzGR&5~Iwz5*3LbP)kD=Mt z=)?Ze{>AZm`u6zrY!m+`l1kJPSk`yVZg-+iq)L>Q7+v}?8%^M<51Y0Pn`6q^z}|qa z45>Rr^>5^`Td^svE!v0GZDP(9o$R<3dd%j`xabfH8jn54LRW#s{Hp^=p(c+Pm2kBf zOcjSJ@{#}SH&Xx2Wv!8|U!n!_q}!?^Y3)aBPA z-N+gzx+p+pwh9&`MXgzFZh^X2bt|J{RkuTZs=5VxMdMaIaYLR3uOf++YkMSs52u5Z zb6}(wht1YDp)N$JE33B7Ak)NEY{mz%t*F2M_YbGT^JdMd zju($++2D`DGglxs7)G|_n;u*YjvX$B9gXhjN#%~K8?Ub)%Ug@>s`piQ4s2;5Gi<{o z3OS)0T4Tyeu}N^h1SP6+4SkXq0b({5rS3C77lwT=mH?-WxdIhfb6n3$ zvz)+IYw%3L@fko~A0S(9H7>|Mh=R zO928D02BZK00;mKJqBHC!N`Ci0RRBa0RR9M00000000000002CfdBvi0Ag%qZe(S0 zUu|J-X=Y_}bS`vnZER3W0Rj{N6aWAK2mlN{23-MLweH=4007q90RRgC0000000000 s006duQ~>}0Utei%X>?y-E^v8JO9ci10000200IDf00015g8%>k0F*aYS^xk5 diff --git a/dist/stylized_tree_generator-1.29.0.zip b/dist/stylized_tree_generator-1.29.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..5c2e3539ac374f0d00ae031eddfe7ef8bc9860d8 GIT binary patch literal 35207 zcmV(>K-j-fO9KQH000080OmdhU7(2oEg=B_0L=ja02KfL0Ag%qZe(S0Uu|J-X=Y_} zbS`vnZERIgQA@)x7<@1EKZHCtZS98Oz?U)Di(+Ad!jO_RpKV-|j4xl6`Rh&NHnxWl za@<|+&bK`n*?a?f?JRp_{4Ou^lBY?UXf?e(9S8jjsv!huIe;LB-ZrDw5rxZ6wta1D#O&kb*5Gj0Q4R%fW!n*m8skx6Ei_Z?f$@jmC=&jh|qH zk&)U`IDnJaYL0tPv-rdD5bdQ>!r55m2X^uAn#@6z9SMUAq_8 zg=2UhOt$=G_XRESjPYmieY~B5P^{x06LB|yRe8b+^SnBZJLndypAv1;goVNC z)%#*yKZ#jh3Ws8l1jw^AiFU0!98q^YUHDa5-Lp&Sf8HNZO9KQH000080OmdhUAPL~ zQh0{|02K2901E&B0AF8eZfSI1UoLQY%)RS!+c?%J`oF9C9k`vE6RBHLB>57@-R|0U ztT>)HPL3~0Pj_Y1lmy8fQ=~p#Y{yeGbspk8&iQxVeA#{R(GQm*>b^M6;b~XT z!fTz17#n}}E@Bv`?Ja}M`|M9|(=1=6-K#908(#vr#3D|<;%AO#Gw;1fZ}H_nzW?s~ z@1A+3FRPX7A_$%zrk?s(mp;}#P4d-Bc&)3U^w2}vy0E8v#SH| z1jm4{caQS<#LvAv%0%P^FcvIulp*M}2BT?w3X}1&I1bYna<|4Ato?W)qTcE&j@p{V z@HL#->+`ejCqD`N2xkB@!RC1cO}$9~6Y&E0L*OU8)jSk9goNz;?9<3Q9vxjCkIt?~ z7YE)2%oV1(^hU{($Z-Ui7XIUhq3|xnB7`aUOYeO=3+4g*y>)sqMx@yCx;S94^bl)S z2-;I1Zbj6gmE&Ew%W^*q(g2q!4d6>@JcpJ&+`(W;ohCRr_4g9CSpQo3S@9!IzyEHY z#7labX928&eDmH19C++_pTrb!Om|4XrdH?gzdODBaPx6|arAx!2M?#AP===S{dXUZ z|E%AZUg1m+8~*byN2lkbORsez=6)V#fZ9#XzyI!J^ycX1{QAlu0arrI0M+{n{ojgU zaht)D-JX7S4ZD2jUHY>iPqA&U|FVNGyk~S!&$8|(k=*(s%%UJYkPTnFjbUHBe?sfQ zHV;()AD|hI`U>&K+w1jryd_}RG5px|=vc)x(B5troAo4I$AEzZ&#s2lUlG0O_g<+- z$B7>S3cQZt6u_JE^K%g`vRimD=xvu?-K^we1I%9ruy%BYy(d^Hz}N^;Ii85h5F@HHf#9Lj00=;YYtkFE?J-7j%CB}y(CWo_ zDaVGv-zE8Sc`M+k|4n#DY4+l`2LKwz1rQfN9cKbRD<-1!k&`5jSG?1DtY7*(tBhyN zG^Ir}x83o6?t6DhE|$xzSP-xE4v%802B%TI*LiY}m(Ea+4TBE>AOMYtdAz{myz?VG z14`v5K<>Oz7*6oH9O@{`5`T)f0q;`3x5Kjr0(~22ahAlZAYJAu(j>%3pjijF0^TeU zw~{J6<;i-yyjtO2c(*VPwD+z=be9Mq)Ii*Te!{=rWt`Df=H<)v!y$1zic&v&TEUqD z;{FRl6%x95K!Jlv=HXciGd)*6eb)ic)569h*n}YT?&0I`9xfEv6>pxydcl>!7?l$I zKdp!C4n7{ik^?OH9M&lp5s>3N?Z6R0e8J0dH5cAHyw7ZOxxNjicacb$I1GR|Ji}Ra zPvEie5)sm=c*-S`!_~1=gaZlode{~MNOUS9!pQ!9muL#n3O+SVI}W|UpgY*}{?uDP zA}r+4djMWz7Q=KxxJ7bUv*{gf$RYvkrCk!RbiLACHHF#LXBe>b0g)bfp}*yH@Nt++ zpnnORWx$Tidk42$_~Il^)UICT6HSwb9UJBHNtfvWoZ7d3zRG++TspLVahh;|f}rf| zc1*ND1e=HiPZZsYbnXR`89W2MUe6fBpeQC3&;W4xdfI_z1qgb&=t0Nf#i=FQ`9kr79L0Jdm&xpWsmrb0byU@s8O2KH#Q zY=jDMSZ07PQNAb+!si^gjBt_=x3$4l^y2_7ple|9yu~6HJ#Qa)Fol90FN|*^rXVG% zsbJeNw}R>U%1=_@v=VdgWb18v^W{!OKx9vrtk#6RIKnE=BZ$#>RZ z$9cFA*3HC&9U|)odjKR1@%=sVqb)=RpjgOWE0`|hIP>0we!{D^yITyyXyQkqpU->V zZvkK+^32F+SJ|BPX!Ise(-(}C{{-xpj5N~6S4eGO2K&Qy&jjln=nkU9p0SzFZ0!9B zc-b&yURxdpAPVyZvB2wLO5+G-1gJ6K_rHBJO z19+0~2zWU#If?hwJ!YaPQ2xn8xcQnbQEK8Nh{6kqsR<3A;H}vb$pRR;Ip7Ag3X$sW z0_SFJu4b$Uy+MI<%p}7}1hfSSk8m&j3GsvPW4K4)?*|b9QCJTu&sd65GXT&-YKSm1 z0im@>6+0#}KM1X>Oi%d^q4hwH`Hr}Yw*3nF@&kRY9>>RfuQ6}0WY93w9P5;33^ zEc|jjXdkG-Jh|FCz3pvp*^y!(50&Sd#yiOH_Qk0WcKdWa_lho`1zb$stLwAT`G{}2J0zO`5=cOTS!B4QUIFX4 z-v^PBlq$gM(#lb1(!VJDz#)lTW2@uyqwDMLYd@K#Uh8TFyNHxec-uXiCy9Y@ct1@3 zChjs2Ti+wLX!TPupwirsIIFhsDua6lZfN}bO(0mK;@!n4NuknZd@lTXcLd^;0DAqD zE1H7af#>4y1yv|y_njW_AXTv)Hlkxl%`Xk&x8=B{3mo>NdDfAXl&)=cD?7M{mlhww zsx2wl5QA-PZri)LIKjEEqiveU!`1STN(#vf0iE-2GhiVBTTP%0scE298S+LYWRZ$G zSa*_GpI}n~P5lW;xg^^EpSb|~hpMGOu*f(=ek?jL2SasP7hB_w^j2TJ)@P(^|Hns= zpW3i=z}AMu8UH@`0*4XS*L&oX!cw0F*@OtFNV)A0c47ux&lu1YX1&+CnFC^X>g*<@!rR)?z?z3 zXQiK3o&cSmWTGSG1|rso6(KOK$PM#&7GQq{2`{t6%WXJ0Ob>v*5f;FG4a>m{L?OP@ z&3YFg<6{mu^HFM^07rl%Un(;sKq#$8i25sHK_V|k=-(0+EaO$%fUK;wLYBOuu_6tM zrL3aETOFkk6JK>oiX{7JRCGy0*KkTZG(eHAqSYCiJ*s+|VaGa!Bs&L0g+oEd8O6d5 zuj6r{@o7*u>^VRzsM(G+1B?4dy`e!{`1`;VXcnaql&d^C-StimnRoz}pFvWrl|C!d z5sp_Ca*Ph9%Aj8ori-iN(7ZB?-Zr~u4y#1!LB}D=a>IgUxPlEuK})JNkTXJFp$D6l z`4O2GNN57gz5#qk#tT66F3w)Ry{1Eb%AA!R5ZG&I95FwWKPnOYL1ymawJIxfO!uLu}}ljKpn}s!UAPTF9y5bpE{NV zN+K4}<0Tt43JnCDyM3TYYB*A(Al(MeW(NorL+7=QU%W;1-vfr^1mwK4t7`(3cv)#F z(fkvU!2@QQ65^RwbFanh1XAm^0v8}H*0*Jn7ev;!8V?tnZ?}Y)m{~C@P<9`=b6T88 z09P)zp2o+&E^)E2OM!B@;XPof6qz3Mc94lx61ux0618%L6;kfA7ssKBakLlbo%MHLba7%Mw$&A`*q z(YKUk;IUEZC2d-j``$HCiGCU8s@(=;8V;Sv^s0nQ-J1KlK&HXaflZbeP`;F2t-F+} zH+*3-y2e$Gd^*{3!H_vi#6&aic!V2}dAn10>km6+^ef?|frmSt3evwUq1hI(WEvw# zaoAy+A~Eg2p<(QhNTUe83m0~KyDnT9?sP2krHKkJn^0lUcQS&*3R!v8hzQ&3sEUmT zU5T${OxSJ4ghJgr!qq)c+HlSC%|xD&wrmX=ysAS3T1OKNwj0o3zf)EJ*6?s;zGrI^ zBzFaRppwsz?mA=@Nu)&3Vf~QQ;JXQf6B+uiY^mI$3&SR4aMOjIPT7P#XhepcT?aPo zH&KOw0~dykx6*#?K^}a=4(?VD@?LqT9?%0t@06m`YFoS(%PXrm4YJ_AEzfu%Cb|!? zo1pKT`?kM1Z!p|Ym}@5H+q>U6(w?BtZky9WvBB4^kGu(MDGZv0Q44W?7%+Jh%gcIUJOz z_Shw3ZD|$z`xU;B)#eBC3YNR7 zOz@eylsnRR0=>;rX;Jf?g2(7=_2u& zH;F22ovrKI>6uO!>B<)w;^E9E$AcQzrFuoy%OP1-3642>e55gJ%ux|Ab1<0|UDu#c zuF)u@b|?87Mqz~wnd@QlPdV$*>(xt6{Zv_Ww8%rf%DqlcRyyQ6ZoSlARWC(XsILQ#VTHS7(+Tt0_y_JU}YXXab{FA-Uu zSPg!UzHpPo4^p`4#Me=h#7S#Q0`{j=3S%(tpVBSwPhN{Y+u=bb;W(J#gLd1Pd7Nj? ziEjou^j-tHY*E1Oc-Q|@05r*ia5jb`ov&I7mN=XN$(XFZ_GW(O_pmRwNP7#BwF=K% zskj0Vp^E{Ovuh%j@x5q4OWBQ-uFj^Bcrd;KSgM@E(L0-*eB}@q9q$|li5AqII(G0^ z7({pUO96;}JJc>f10SxB90V27#ip4*w3@#ks)$mU#$}UjX{v0>_@|tbuV?RX-kT6V7N4sCyxr@YfYJ)+Q`0f}_DxXbAzMCL zNCoIO_M@+@A2cOMOL)bkZg@cIY!!TP{dLu$Ta>~VfXtI_96dlVV8puyJ0r&q1o7HskE;N*q4N$N-O9ie_Pahi6JCFfHPW_(lVw! zIN!SaGO~L6M?tP`yRc@caV)ld&1hCz8nZv>;F&CqN&NJhv8WA?;TnSZvvu%LBQ?mN3Wq6H zG_JMPlGl8%W;7|0tec+Im_UT38uaU61PNoa5uqWB*4=h5n9PD zTIvnhb&j_l^JJ@rlfi;<^NLdw7-Jj$H>{zD>xZ#g@@RuS*WPUak!~i(DMMAL2i6@V z-B4{^zgK1ibhN~pv2;6qaSyAvk!1VV5g{!cknVk08OGGHtQpH}+CY0u1@A^9a#J3$ z?}E|L`K%exXsNbg$2}Z-QEf6Dx6}u6SU;Xr|7<#_Y+lbGu`1g8A>+g0dcZ`-{s{)?cBO`!#0{@sh+YzskeTh(th^o zWbkkc^4A899ram(ow8$7xD}wXd&7hW^2X>G9-c5^T0>)V&0!of-8qnRB%=o(nC=^pCgVg!n{j*)Mop@C z5e_EgfhHi_&g&GZ^wPEIdI`8ilMImoysRQUZ_%;V+x*5S6fUv9pVuB4p_I>o{r~DJ|MyI3m z9{b39Kgv7-vf-g8!^H_UH45gSVHcsLM4=YC&bJMV7Djnseh`W%gxP@ngFz(ZhXN4x z3{whp5`+R{ww;!3bgPHD9bY}TEL-r@+Ut)WK94S+o8xV6Bhp~G(6boGG8mX|AVpN>Qap)@e&*nLqJnC`B>)+~?fXO*ZqNH1BzKu~ zXeQh517b9uDGWg&H+%YIN5^nNQ_3uo=`jnRD;4vbC2{tI5Ih9YQ?T&>x9=?}^iU1BJ-`P9eTTyeDMk^qP!PG`G!*8!A{YM_kf?EnXH=TPFnV$+ zEpSFkWdC+NM(%=&VLE#lk{hDxlQ#tdfbopf{UL(4ULYSO$&3 zO*g%xL{b>x*7M%Q@Fat~>g-~K6d+4Wc`v3x-L6i93iiQ;JRrsZ-mN3NJou*6cY|xA z7|hyq`p&9$5~~{2ET*z*Ce1SF;Bkc!LVz-7qm#?CQ;*YX9Z zE@F@zx%Jl$htv`G=7Ko~=|K*dfd;f}0@r^6bajTgV(wJ%Z+S3{Ks|2=oDz^wWOtae zF|Zhk@UXkBK`kS~s^JP6npY6-6{3J~W{d9q%I+*{cV}61Wfr@$TubW;fvc?Lgw8e{ znc7OF;(ZmP{92g(3d}ntjPh-mk4{d?=feNCbAhX1!tK|?T`Po4=k;(i*z*mS8cDhd zO<63#`8(fNpk;zRHNpUsTJEX>{oYV9R~uZ1){UK%<)~z8^HgrmPqI{sqss95#1LuC zh=Zv83ek5sEbQe*;yliSr}79;)``%kC|rtbRmSoInI#BaoU*dB?lWS@NX{upM1&d> z$^vkL4sc=58xePdxkS*Jox^l(zD%C}3^=%2u@&AqB*X50U%gx}yPCH)b*WzLrrp|X zq8C2{{-72w=w(S|u&2dqdf5ym$+*Vma~~T_+UojN2BiLD1Elb>tbr{c#g`475&*%I zv4Fr%B~WQ!>KYA9ZJHT~dr%-6ULcZ@k;yInC8^SS-dhS$)0Y=qQ&C<7%lj$CyQ143 zMrZ>n(j_<%z{1bxC?QC0gaUynoSJj+FsCmuGMU$MB=E|Gk9gepWfM<=?>Ev_)M-_W z0rZ9^^Hn{trNc9PX#(58b>PeTtN2v`DNWg=Tl4BAoXnT?5H3sTXf5Y*{qeSqwAjT) zMw+Gt;L@>#em#KpUAt|9S9{6gtBp3Arf5M|y;rk2&aPguhi|JeO5Gm4Y+gip?#=Vc z-O3B;H#9O%bp|4OEwu2?wlTia`(w3ieAcZWLV+k2GGo1ieYZAz-T*duBdvrNzzsF|NcK1;!k=GxS8V# znIPO2*(?c`)oBAYTxw$_HnDdK*^2F9b-vc56!6%+GF4Y9SIKWsQjkL_d!P)Nh#c4? z&ggbXiThFy1dJyNXH%Sm-b|FL>b`|w6Yd=xNj}4Juz&&M3n)YiPs-)F0P+jsdC65wQa%Z@(yPpjjgk{ zNKG6*=0MR;RaaT7xi&=CAZU@CtcB_|L{+$<&r##tu3+cnTO0-^6^{Wz)E#XpTk9_{ zsW=SmvBkar+HSN{#~r+h%?Pld|IgVara%Z2*(B(BzRSxj<;}8ko4< z1}1L50nUeQXmbxdw3P57ky+sfwo7r+dvvoELMH2xY6~-A5^!iz7}1f7DzJ~VOPFU` zANtA0P79NY*8(jZ`ow7D@N1bTd}m*)j21h6^>V%Wwd7(-y&RUzS~INCUb6#}20PlM zY)ISLDNVTEoF;p3#J2GC4FsFG&1sEU(~mJ3lnkg{=9~*k@4p!_sxbwz$BG_B|Am)7 zV%7tlSW4&MP?B!zpZ(sp^5&jUgjn6`;Y(}z_WEl1)Z1@nP`Fz~-3zRg5`hfZ3r8*GK#KDCDL4HJ>$oFl0?N;A_gHoe8D*jbRYR-|=wY zYqgMap!?EZ(^;O5Qtu-`GRJ2-7JFB-wM zh372;)ulfww6)xwG-l0a%~OH~l^N~!<#zA%?V*~?3FobQTYrG``6Pp8kgW2l6j4VE^X^{Ze~Kg%C#mCvx8CFtg-PKO9^qS6 zBIi1mNDCWKX*9}1k?0mhNIsA6w*JyPYg1ko;MIo=b2p^sxH-QWTeBuwxR42XA92kb zw<(Qp`?%WF+-J^>@6D65*Y8H~Xyvni#MW7a)P5>Fn35EMJ=FihweUuSl=4`mVSGls z9oZqfCvX=9vStq@FIN}s7Ey4;V-=ZA*fj*bp#0qPm$uGFlKNyIhsEP_j>rwWQ#1?RNS!uTmsQRd`pINazS=YGK1X zEW7F}$W6ewpT0Qoy43ze6^&?chH#IVf>kR({YI9_4B8VxD$RVNn#~ zl7-H&XaHiUWOa+;0Gtb0=N*$g?Sho$mx@rdd*#wKzo)TD;)B}}{ zmFhgj2oz4HCI-76EP*Svs`7Z!)6#2ER!9lM^l7z0JOs3|&|kXt_w^tZhr}xZf*7!C zQg;O{sx-q?UEvojOZZdOmwNgX)mrSIA_q3H(O#}r$rIi}Lku$_oR3#@xAdc~R zP{1~=hi&Sh9SXK-iS<6aFf5t)x}NVg6dg22Yx$RYy<07DM}Li zlpGzNY`+1cMX`oiS`VO&KZ9Lk4YOv~R(!vawuokG!I(O9Mb&h)2Y5-hRso2;nVQO^ zekzmHwblc26~LEGi;eG_Jc|cAgWXpJQk2$w;y6>2dZ)HY;97G9XxW5(c3~W;Nm}}H z;P`SqI!;RHxMwZV#`vgXY_U%XA15WBtce33C-t696I-SR&`bzx4X*fVGhCdMaB)&K zxK10q#Y*mC5EY~Vd9!l)NA6Jv<))F4i==tpnsQutUjdCO}p z#0nTktSW?62FSaX(@d%4jNYF|C! zGp0g!B74GZnde2J0;>6vlI{-$BqjB>rQh!)jG@~S{X3~DMq!urH;V@K%aK;Zz3lAk zu<3{@YL*qtAc6|aJ2=y;OM^jU2dh%pRH?y?WlHjPIlDlk2S4^RU+b~noPRjF9)B9W zzW#9e+)_=mu~S)bwR9X|*@kMwm-Q9_cEp!%;cF17)n|Nnkf|JmB^^urzPj$7U7U=5 zJ@Eb}wCJpz0GOXw1x#e6AC`~N!vQmZCv;XJ}ZQGG8bin zN&zAc|CYzeEFv{N2nI{C|0JY=Ze3vf5{z|$Z000RR7O=aJkrXf%6%Q{AW|muh3LM) zq^Sh3c=Yc$+2xZjZo`S6XmI)}h=eP%YHtfHYnR+(ES0J=LR|$_QW~f;63iag>87zw zlQzg-g?TMML@ymDqSU>5qpGeOF~|g>rPwq3>ifV?vtYLQS;p25-hM~hySRnpH1l)w zIO7ZJ6j#oxK4sXs;Uvq~wMRM4#=*=u%Cx%Gvn+=xALanf;0}b~OX5RRIFDdUN1}#` zZ%%bS3I-g(pkyTjoNa0=^-%yBz4!vIOjLHTHPdSppy#Y>tRGEXLRRtxKXiFf&^4;S zO*sLBA3D&gvT&uTV%`tOBI0OeY^ia7k>)e+;^_5TBFqvxhj!;WL4_kx6si(Kpt6el zz=K;h(HFP6N>%(C*M=UQUw73T!-xk%JcuBq3UEyH(ej8|N;E%W#s#HaWx}pPNSK2_ zTU}DHE}yX-2EidUH>Y#!qjo6bI;*^VSXns$+L*CwD~#_@EBG3XggWA`MM14FWh!$; z1Xn72fSbZx)9s#PVL#r+SuBeM0vT+bQ#Dwu_S2{}yBT@Ay?ucZ^^*v%kq@IhhXys0Y zV*hrWuW_lPCXR_%i=A8ka}COLfbMba%K48ar3&#nFU`#@g@L}l{BU(O{&@M}(pSAwVIhqHb>d9y5;Juy29eEKISBnI9umE zz^&}bkV4x=Uud%4b)aq^uy>8a0Cn*nn|fGI0^A;#wxNUNDF81c@vs&Xp-tJvy}St6 zq(+mq_DEu1=cXdyrMVbw&CSHTHR}?ga5G!!Lh?g_nmQ3mi4W=&Br-)u1y?c@wFnhH zPj@PSDLB22@1vyVHqx?Bve{LXxG$paKohcLZN93cny^agrg!X7Hpy6(tWQ~`71fDb zn~-dtvnsKZR`DjROIekaQ&uSrcQM_jlvT;Ul(H(R%UPAw<*Wi>U0|uC2OroiV0Do- z1C~ezN_Kxy`Vz@8`Bn{StX0quGc}R16qwLf>L`&5+|Aa`9N~ikt*B8$(`B2+bU8xgBalb zSMJXe_Gv`BF|cGWx6E%Q&YASJI)X+Nu_ls;Q&ooLTx`b;t#;T%=^1RN?rtHm0uuVX$AtFk4DF zZM4xiHET#6GAri}kyJoJx-Nx?c@A_U?U^Hi2u|6+pqVRo$Se}IJQ%)nVZ4MB-!{QX z4eF^?*U**C%1K7dzWP+m?!~m3yT%qSE0n3nV7Se1VJACYMV{yNN!^l4RySprE2u&> zt((cyBvk`UQj^z>0Ih9k*kKZkwOh-_P%E2_aVvcs+)6=@U<@}OlhkB&Q(YWM-OPNc zs^FT}dZL^_XiUMsP+OFuMa8MhxMJeRTK+=+Bb6oagx!} znkd81G2YgNzdM--xXxorOV{%*{9Az{Cfxd|kJ_4rfNO%>q<*A)DOx$DvXui#Ul3)- zr8zx19-ZG@oO-Q7G30Hfz=*GgVSW0liXDrn4aarOG`=cCteIW3FySXOnOaUv?-l5>RV;LL!muxKxy z$vDjJ8}wG~QeG)iQzuMXnVz8!HNjvd$W*hv^~2iSK=iV7PB1tP(50_me{;kocQZLg z>&d|F7p%@@eQ;=64h|h|ZyUg_j}DddF>^iI!J(8CsE%JxU4fx#LttoH4h$WVnz6v1 zv?(w&-8e8bEeD3CuE5Z=DGD>KL}3nh6{f4V-yDURmZC5feoL-Eg`b{`Zm!SI&_*YN z0Vw2DrA8{~1T$5PY}#_&q>!O-L5JzZ( zME?d#vs7>{{a$|$rv12l~c37#)w`)(fw9`p( z%qOq@pvZKADL98kuxVNjHcdI$G_3@i4qwWBbOxIm(Wa>@+LY<#hOwq;CDxRuVckh| ztGLrR*F>Ar)Lr*heYmNLk35jEOFFrQjOlEn&zEE|)h+~y9k`To+)`0CN4mfDjj*Nk zKOMJ}HregdYUI-Jr2%7o>{9w~3tqx?bL~w-^pZS=C}_bcl+HvPupZnZ^P^=5`1>wK zUO->n8wK^Gm8c$GmhIv+;W0_XypbHjd)M0T_ss_X8d1ca8%3v=TNl*4F)%5u1||*n zDWN(=)u-zNlhR6LQtFCKD%PR4WdZWq*rc?o357kAU>oG19GjFj#U`a2$0qI6c4!Vf zO6voU(#;)=(u#-C_F&f~nx`&*$h6VBD0O=mrST)uY zg3zWuMrp;#i1DVncAI$_rA=-|g@B>L6({Vhi((BsBO>o%GA`^xiLO79cQw6Df-Eak z*W|>@+{W}4#D)c&%9$uBEKZT1=^%rXGiPGQ@SEn93BFA3NDXy_;TmA#-kI*?LO z27#4wY(kgTrA4b*>>z*URQfHEQl`aOWaRA0{YX0KNWdlWuQjL8Nw$ zkp_kQKUr%+luaB*vNFsjHbmL9!H78M?djt;aa$3KLDriQi?)Rw5!QH}A(5Kclctb+ zbJ9(gM1K3trbM-pRa>GpV80M&8jOMT(y?*dQk?l$?19|NQO>0{laSTJl*5);1LLvG z9E-02$G1&@bBo530y>A8SiCtk5s=Q7T5nwK2(s^lZzBY9rR9iBh#-9KvG7q|{&_b#4QB5=9U&8etx!q01j!}1=j^j;F!au) z8}L357%IT?iS&fXb7)zcA7{EwkNrAGrXgKI z(`qFMi>tD}BeMe{GbPeE%q@1RxQV8Q(A=Db93^Rxzapuwk(~SJi<+0m9KlwFc#~N1v7Oe@N7GokySiLId^O>kap}2m5wamX(Y2 zyPN>Yd^)N1UzyaW#Ao=E$p-n(%f5BVq$+5tikZp8nGI%ID^^b9`c(1^CnHtm2YSgmV+(`MSDu6=+^+XlN_q`MRV`v#3;e zuCx#S(phfKnSCkU;O?##YkI}Snkt#1rVfF*bZ7rW73N%*b?K%yYQo%)bNDhjH_1Q% zOce*bSCw!MH>8}ynmJVnXqs;v4wmLsdfMMDtrRq>hJ)e5iZ_2X5m)MV`$%&aTV#k) zd4u;qF7B31oeRzJR$-?#hAJY$O4+c|TrpU5vuE)bF8$lq6fr`k597aK{Bh)y_Pz1e z-`d(DcehBfXI>E9g+1>fPQqCigovr|Za$?wL<9<8BEAP1o~0l5ptnJy(~U4=nmQ}_ zSQIC{-y9|G>&iWu!?`fxej|*iU)lmY?$=<)eKU64Ti7v88<6APjU4OM&&kZm$?RLJ z{;>I~Kh%|%Glz4n`om`D@ZNn6AJ(k&L-R@})=DSNT|QsZtoEo zZN@E~*nmZ&i?i2nuf3(XO}t=9)%Y@VC_hg@MB;>RKyQ{<5|<2{k-9m%AfZw9D64Fm ze2F<>bQ+A34Jh%~pMh$Y&TYjR>{v-HC4Tr0{GjEk-ttv*meKr85Qg4sf8`QwSTSER zbU7TX%Gu?q-gCZj*jtp>Wq2%K$C2}<{7IFr{)U?_y5AhU`+D%Ut*wdf8^QYqQX3_k zgO}8Uw`1L0(6R}<4|#60-`J@lC*6GV_>|fWELiXBVZCpHl^w$GH-eQ{@4mcx_v>K3uYozg zZ}t$(t0(D|Mh?pY59^3CFc(rg=xqJE#ZcYXH~EW|I5%Bx&OB7{S6-dgcn0_I`orbL z=<+#rtsbwTgKI;wOINF#k65nZBbE*4^ybrhe`PTexhqvFjZx3YFBKt227a8OLB5ad zg<|PTcO=)*U((^_Xqh5Se7y@vX&R?GASbxF##-! z$n334t|Hf(ai#+`BHA2*HnLkG>}VPzQz8ol`%4eK*(tdJZvO$584ac)^&<51rsBbX znOX}hU2#Z9OUaKYDu=9}9ZsuBm9PsCIpemj9@Zz_t>jkbn;8P%cl=n009!=)J>>ZE-d~F3a3F>Vf37y+P_Pd;f zmQ^tDuF27Z;IuPFLXWC03@d&WStE-knqtmtt#NZ(iM`9|W+iluDI@0_W{ku+nrmOR z)72N3E7g|U-8R>*xwPD-mF4EP<;+n}uD+aHshZsGuDN&(#pLE2m30N!?9_pdN^`bQQ=s|4ZHd9KcVvQ(7G$ALGE)7E)B0lM^q2Nzm2Y<=xK zwt<|H+jy@xpp;iSN!;A8wa&sT*EwVVV|;0cVqLzzKDrpzoV35tG4iw0QL`KUT543_ z%d$2E5MH^Cn*?&XE&!0GVJ`PFtf2<9rdxIsgBry35*xx}3z26%SciKJgH)xyCO{lWQsMM?>iu2xG zOukEG_tZvF9V#YdfPi=HixBI1sNg0#5hhcj?3VKgcM8_AI7-LJ5TU6J_ZhdDtRQfF z`0n!Q>VltgVSX?(!K*_ws6Rvld;9nGWy&wt;ODADOrb~V2t{#Sivm6UVLg1dI(_%m zt_k08?fDs$zCLU?KZSkCXlFpVT@U59UDan`t>X4tD2F8|x7X*KHyT-)IV;}Q<|%3Y z`WHBIzbRzSBXnBJ(79K#ne)f5&0-cba1gY8llgKGP3Tk>uv`S#XjTYW^C($1TfVS` zgNW_(^5e@!BOJg2AGS6C`&4e;wZ1g-vMbHr&?Kg7+-Mk36AIgjh87KRsUmUB%ic3( z0c))xu4rn*UkZ_tLZ%_PIi<|vz1yi~?)Is%x|Pgz^lp;Kj8|G%tOUlI)lEuLW1;P% z!Pxb#5If60Ksw%_G)#Bbl0M%You7{m45u2E$M!B1%VoyVlytoG5P zk-^ep3mvL_VlsI0ioR##kRhKH>DPM>bc0gAQc54MW)$1r{hTn*)0{N8`02s!I7NI>GotC_KEZ+A|F^$xFpFa zx+|W1!tr=2e2@)qn4PVUDU(+rT$sd21j$@Q0>fsGV`;57!#RLoA{Qj*hNJm*eZVM;Ff>F~&NqR(6&LduZvBtXJC(Mtp4qu(=Wr zkH!ra!2qOwfKFiOhIF zr6+HFI>VTwr3=&oMuQU@N<|oEf|QPWh4Z~k#GutGMfL?5wTIsFtgo72ITdyzj4&Z& z#u6eeO{8gns#ckd{}!vP=q5h=8J-r4|C4HHqs88kuiug*sM?_qwDGugs&`FxK;!l) z?YXO?hI|c0q85&`xaTHE+}uHp_{GM=s9eREySvDd_2yrqM#dsIijA(De*r@iA+n&{ zhz>QbfZls}+DwS3K?&jZEJlM{tf4}3hK^drO*BX%o|6Qro+S#j*+HtFqEfhC0YK>x ztvx|imawK%nvu1K07i0x773&qK!+$jYmsgog&VBg$x4Oy$k}v>ZF1TNV!}^9RC#Jy zDWHSlhv z2zja>=l%@73G>ZiUYQDHS`Hm1c3k%Bn74t1n&G`GtJSb_9{{$w<(T2UvZl42B-w0M zIC*>T0T_n??fF&kB)%<&3lJ+_FG9Y;ABsOo4?P@J!*@S)m?L>f3YQybR7zzvbS{kg z3N5H}g3OG|!NymCG8qnsk)#HMUxAP>RU|6G3s4}v=f8I- z%Jl>;$=pi7SXYJTMwV1~^(U^36wN4! zBrcX5;V&OBr`VzQcV1Cd8}GogzR+R+JYGt#^}u=a)ph<`Ahq*LI2i$XR?a~-zS2Z4rw3MCCk90j+j2FYAn^>vg7ApY&VeayHx{gGQ`2)RE z46zduI9uOdLO<_^A3T7oQ(%I@SzjxJ9oOIvXiFqr9^kZZ0gH%; zutlZ=|5hyv8B54JW{b>ayB+g|VMW2W{Z6?|vPv`b{_L87VjP0+02r$|@_+0agM~>` z;QC>8-os+inIjCh3$_xzaGoJ5=fJv%W%ZN|daoYws8PYwE*;-lk(KZ>sU#9XOUTjx z>fGi^Kf&%ZY8j*mDgD&T)X8qda^?Z$)iM^u!eun^p+*+u06B~6v(cN;1NMjLMT)}L-O>+b`86tyX1Fe)KMhk=oCk7$VVIUylQT3{7V53caE!-_ zL?0O>4H&xQvve*)cPK&R1MXtaJ2J3<&z_BmbVxCiF;fjV9aNCSSRRcJI_a@2Ap+Pm zY?~HeR_K*8F|{ z-9MPsxuA1PA;KR)Xa!2DZSU&lIrsAkq7Cvg*oBM?5!3Mz`iBI|Nr=-*0=b*4hutqYjnK!@Rsb9# z&eDU+7P#otn7p!7T2k18<9U?C7)_Cvca9Ux9)yV5(WH8Kc!o04x1~yp#_&7-f&g zvS!q6w~zX_mwoRmG)ggK7OvGTqMLMpkdudF5Sc!;-Vhd@uD903$Eyy|f(K7#rB8uP ziXYlN78pzshKL}04D$rlN;7i9DmYf_WzA~!I}TofHsBvDNMg+60wBq5Sz)@-4>bzu`i*C1Qc)WMDOJVeK~Suo97C!;q< zH|N(^?c!B0U(NhXv_4*ru142ay@kj$Tz~(ElA%FPR%2i%r6Yo8OaHM&=(h}_)&T#= z+S=C!{VGz`(UcAC77B(kJ*WW&N%Ece+VQ@^IxogN2op~pE&zI-w#-1$V2(W~jTW|p zDU^bfrZLFekr~VcY|Mf(mtgDPq4$^K9eo&H$u>V&kQfmf(1e@*=(S|qEnjM zfD&n81})hXZlow{m*9LxiErN&5-Ml4g&pv2MSz;vE%tU#OKb~W`&!B>h9gd$({hs- z&53{6015mT{FBPEc__F@X07mn=#1|7SDVG_eO2eqHMw^UIvvPO!8aLnd7 zn58X1qk?ddc?H9u<5@5V4!!#h5We;Nikojg2LkveXL|yir}V|uT@tJ^83r5 zMaCJ!sU^J^f?hxQrGF>J@#Jp;=n2w2CJu*N$oinR3^Qp3~UqUr;0i{|4{Z*r48zg7qG3%vM5s4lqpye`< zx*vkliYE#XoNzqBMD?@*Jnq)%#TZi+!NNYj_yF|yxo+OWkn%CCF;&ouw4n~L68F+V zQN3KeCCFNrQW^4fSaV4g4K}-~N(W1i?Y?^XsgZIKb+Yb+i`-MCgu`jVAI6Q;0eTOo zMZ)U7gRlWCOX(ymXTQV4-|xQ2qhH;}`#?O5L;ovC{%Qx2)4Lb6Z}xea@Nq$&y!-F_ z4~L@~*FYgb5;zX9)Q_LcsN{9ZsxH*H*y)}qMX-qe?XZ0R-TCO~%@`Pln~!vIF|$&jnMY^RMDSR^Y(I=QwF#8HD+IGK?m7l3fA?Sv;9P%9E!hDMgcvjgeM|5ZY=is}{8_XK3hIO2t(4K?HL_^7(k1BhEI*^|=Ir!1?*J@1MYnVen~)vHN9=kZWS91D)pK+ZN2IX(j}0X&fsl?PzE%5;#1fwMG;S!PIIK@VB80xtiyM_$ zjS_(?t|;BvxTZ+&gg+!s9#lehRjr+Q8|(~r zUzK4)X2lxoEOJ35Q&nTI5B#Ue*FrgrV*gVl%oNj)J~xY!muQ!w@YP%vtPe1IDyN&D z*mcJ2YjypE%r;O0Byk7y8RelQAAVu=LQDctNwHwi6H~De*J~F3q2`L+tIkGDGR~rT zjF4j+d)6<@enu3jCq&_fhJDcIhz8Q0MN{9EyUhL?wsCx|Tl!oe4uO`vb-W1ppNpCh^wY+sF-l%7So^BiwI9#i?}m%FJfw} zB6OuvY?=l(c{s|}l?7g#`pW8liMPRKeWiP3OQ)Ej%vl|sL#ti!m*n67l~ab`2KPs9 zVXvC3T&qz}wprubj=6p8YecUu4^Zo8C{eiTE2#T9Jq_hDuBj-Y$Ed{U5T4ztOdy<4 zTb3t9smS};E3OyC!}^=2)AF6vLl0D+B2cE$@;6JPHC?V6BTwLcX?KSvAo$vu%nM&w z?y2Xat1HttRi%CM%W0pmXVw4oQ~9D9ccWZ7F7fpe;U2M z{&4xcc0$Qxh^&1X?I9z$g*{Hsk6=NQhzGAsmxW8`hFQi&2jPA!Ud~Y zWH~EWq^3^g!1(4?MO^$n$V%&A@*iC=mF_+@b823OrtZBBhgoKIO``Vm7R|>>i;nnI zr4BT3T{|o1fpH>$*F+_is*gQgkM8Nzh3!QqaBNUZmt9z%PRmFh)}~(xW7h`l5`u?u zW4@y>-YnnICtKItjIv&2VT>6V|ONoor?WgQHL zevFSlNlQ7KDkQUi6iv+f1D(of=~i@{hT zd!kw+I-u)X6shJ&=Rj)aJWgg2HWZKk9s7YlS$uf-4eU=?=6|+_X1HfqW`NEvZ1yK? z{v384Gc z_0h%a(be;2(exUQN6mtMvkXUIxK7T@!58U@9TkpjB$z#}=Z>IJ^}HMFMGb0cUzIt6=4I}PhL8a7 z)MgntZ_O(FpwV&nOASS!Mmo=wtVUWxR`>}2%S1tb5V_PfAFPzuJV&-s_ZpPjms2}! zUW~I+i*v_Gus5s3=_;I8p+5u?(|QSjSH19{89LO8M@#aUo+dp^etLp z!*7gjH7)5=(;-DLI+JxsNy~p39%S?o$(a28!w>PjND@pWWf=QMY&3p>zJ;)HGE@g< z@-s4@zpy1)s%5p;hB6Izl_k0G!7F*DwG@y&zi06+U^6v#Gj~^O-bwd@&|vzc(xt*hn22ov{$v81y*GQp`&MYd676vE&M2*b-#U zLZAGKDH1IJv{Q+Kuvmtn#IJhGRe(tiXb|P(4yl9MOGPHHB8M$h_cG1dN+E()59yI; z8QM@K1MieGVnGv}u7LkNM?j}r-+#cd?`jo@P^KPFywT$-PO<}ZuLfy!8HZ0);_?bz zT)Ifr{sZoO6vOo4L`K*bsbPwb_9!HXy{In5EWcx`KIUuJ8O|}Z9bJh=9URZh&we7N z!4mdMx)_mYE<->HUq)i~pB{ikS2Z^ycJIN_s+r%@Ac{z3dyfu86n9Q}*ZqiV!zey~ z=A*j+yGltPD;%(h*_`<(7Vw||0$D65%83HvshFln=;Uy!85SHCtRzc%a_zQtW$QEG z>QQ#2BFtDMcw+nJ?b!^TE;JpZUfz7n_BEp zi=Ew~#Zb4v$h;way5AcbEw;b^uFbf16~>rK@sMZJ;olfVqa5BC-7ZCdaqSomN2_~H zNz;IIq3S5{7pd1(TX{3;vP(U>{+IG*n_*=`DqtdflVE3Mcn`n)&gGk;Ch?qsTdO!NXzkG zi}pb)V;1QOE(|PchQVbiFchS9gj)^Q6DEUUjEUuf1aCW5~bi_ z=xk}2S`>Oq)QJkt>!@2*E1vtNFtorlcAAFB%O?x~JAeTVmfktS*7lyLCzq0Ec=X52 zoWe3FoP&0Fuy^?a6HJ_FG8G0Q)q%GhY=f61#`SVit5l}Xiu58Ki-(-jLviMCJ`sP& zt%IEmgKRv_fmSWYKp+2*i=+>Z^b4fs-O((_G{em9u-XxmSj3E6M~TDcD-3JK3QAR; zNn$GfeNkOnyc5MZs}OF0*94IKY)a>zqpWDP$v8u-A*ON&l#3vr={Lu}cICGhsEX$O zn&gg0d&+k|O}3eJ6^d5GyBCqZM-E-mDygaJOP{hgjG^0D5P7X=aXKvF;vdY^On?;{ z&m@z2h_foVfn!XcONknpSa;vWD+*4KrRcpM8OH@>twyyCNd<}x>momw7MuWd1?DJ0 zt{t#d1`OaGOf@Gx$+=sJ`ehannY)O`IZ7rNhrqeAd&pvH@r$jJ^8BTE3jw_4yMU7> zU>pRdqX{vs1@a-caD|}f{Y6Kb5+#5K-XEBo^A8y+kr6;R&muuFx9rI1VB!}jVK>L$ z6Y>8oC)+65WB(u})gLnQcOmn976}tB(F0pVPr#~PWl28GE-#KSyq}cL?B|G70>BA*4YZB1~A-X9`qp(a!x45WH=*F8 z;sRqa8WU!zuXl>nJFxJky?RAaIe78Xg7EID3#sk> z;_aI_E6~e@8YL97P2%5n zI)I4il_}WV;zPx1$?znYcDvjNJEBfh zz;t;95xXP*wxYBQ21i-VBBE*z(cG5r$y5e=JA6et4GM-B>{z#T=yNfQwL=YfsKGuC zvb|Fp$~eMh0e%W|N?PrP7LrELve^l%sSc(i}OVbU5s|l#EO!8A1{v?O$d}if3>;`U!lvBX2?0+xbfMs8$#~l+Jz| zUCHo39dU`AbYu=EuT-v3y|XX15qOibZV0$B9e;@wiOFUjaUwR-8o@d5nM2Ad*QvVO zIe+y+Ck~b6JPweUO5T8K0HA{E961IHKpk(4U&nCTNES5#_<-$8JyOg1sU)bJPMh7W zkXPYcQ09QF#$bgQ4I26+ozpttU4V(qVFiF=^eKgiK2K~+BQE= zSlWzI>o8TOn!-t(e;8e1M$&?-lI)be)$l?geUIjgW`2V6&0?$W_&0G&fN+#xj1y&I zdb2xAtfu*mrInE$)o>MvS&;d_ z56dyq!{2!nhU81zL4iS3lAK8o*R^GbLmJbO$$iRvRN2HL*~qAnLFf^R`zQkz1AcL( znk7CwUjl~?0&ymTe{GhVbpIeTu%@HLy%MDKtwVLE(IdJUN-%vf39VrluF&7gJ5{Ea z*A~b6kC~3%pyyN<_2M8<&>E#;u|)BW&bLhFNfAVN^u3R_U(?{OOKH&3|9~NhqypkN z^XVhIq9L-SN-U(Y^{zm9)Kt^F^5AQ=y(j`KYT zw50nLG6QMky}|7(w4N7IPxA5R;@vgH_lJTK{h}oKhj9T>xD{*nJ|qEfxqc{fO>l|_ zot=`>8!~9{enH7@qZttksaK{HuyEojF{33GPLLJnyARn(<60(CKg=`C}!u`RFsPwyT?kiw*Z+_Gxl20nVn3`t+Yd>pZ3SPI%ca76OK} zrmldU0V&M{!HL}up%-@tMwi#xAz+iWfHtgx8B2S1+tMQAlKb|quG@uDeA@50 zzfuM*kVb(fN!$a~l_dctyX{fQ!zgVPQMC`Z0b;&|8^07snRr1t#?ST`d;LQ3Q(DL% zP2&)*voL_8MhogkaLD=hArH67^~Ti1LKiVx2zo?+z@zpiTlxEab%E4I+>=z!sQ0A_ zDmN-@iVn?N&(&~*e`j(FwCX>Wwr*Q*oeRMVmw7UzYI)z236X&z9Fg(epU4|l(mu*) zquAzm=cvKEGz*jXdWoH;z_3pD&j)ko*@qpqz+#rs_DUPbF1ZLV&vYR~CPLyo04i?h~To}B)d;wsSS z=BEMq;Q%6VYnpSr0uML*`4FLawJ{L51k>Ug6poHjEI?pF`#OIgKrF$6<`w#UwKJV1 z>i>(}gah~4)jPz^t;$x3P@XeMxgUSoZxj1{T}l+j6! zh^d_Xl=Y@FQnzNPz6dGnJAv9dLjPj)ECMovQVv0fd@u)00il!x@=bY(ybO&6l%7=Q z8k=x}Z^}E3{bAGSQ>3lc7WNq9uP6hS)XT`5!Bpi^!e?q&=7HfB_Lmu#ppeNmrqgwV z*=1)o&KK$vS3RJ8zO7P3BGj*()GGZ!2!6yNr0w_z&lpGo4DSoGzI;|0zOeY$FyRu} z*WNe42JEV_d%AfV>JS5*C-dQ@{=I8hO^jH^6v;wYead}jN&7tuQLVhGP3_~ zt5kMNqM=XK$_pnZKrj_Th8Fqac8oR|ZZ52Q#1Gc&QDsoB6TQbhh=5?ug)%nGJ zW*?~D6G%0)9!Ityu!JBu3;|-ekYtp>p)z6ls?bEYEOQ#Z$`T;3F!oXg(^*1usqIEg zJ$;WZqcshc>!GUBsE)!Xd0YwoR&*f(lAWE%q9;9bjTX{QTVXUc6lFFhKyHv0_jiLE zWVZ-nfl3d7+e9)#J*3}AzrqsYs8T6db+wj|Ds}MOMhaW{c8YB<7pDmR=IHgvlDl9y zB1)F^0#<~mQ$r#(%tnjoe+WFS1Ndjw)yLJZl>8LQ(i4`OGg=LTaDrjP&e60NbeF82 zbO)m^a~i-wEXWUeLNJbyBXf16JAaiE;o_8RH=oju_VW&vgrFU+~Faf)1( zn4ps98E{o{(lIV${2Ijo*Xo*U`S(5 z#6=h?l7sbn8|0d(U(<>@_X<@<7&_95=Izb8(RwRVvvDuEUhBFsa;awYtL>z9!x{vf zQ}qPI2&9xWTFr7SpKu%VC5Y_gz{i{`++nEoX|<)>;arcS91rV&B0WGtk@&5aCX$Lk z!qG@)$PV9yPTB z>MoYG0KjShhd*8c85XVjITrwG76T-H5WVrI(rU_|N*~ammZZ;(r}3c{FDOqT?=5q(s0-0uEgYos5O0>QH>Z&Ff#gmQojkslqsQT%HMT7f>!yTn&0jmC4AY z{S*Q`_~OI`9Bb`{-^QUx$8+F^_?)sfAG?A;f}wtg{i?dW4xgCOx9ufv+2UCtF0#Xe zO0GQ-7}g*(^K*qh8DWF-&TeM~UFNA|VjQDWWV=P1gT#V^cpT>QEM zAv#*<-#i#4?huLbv~7GIjo|w8A6A@wAM5@_B7}crCrXZM=&QKA$XEa@Enpbt`i3th zMEJdR*YWOvnOX%IdD6GqoW+2`i18fDVkcjC@G0iz_b^oZR76)ILwj`EvY=33U=R+h z^&pfw+~N{Im=hgilMhX3m2;mEvjcOPWlx!kSYI;E0DadfECE|Bbq*N2Q7*5$Xc`nFXNjr=L3569BudGOeU zKO;&Tj!Da0KNSFXc=kh{{l3cDoUr@QK?&O`Q$I>FW3a8QnyI$1Aq9^XQPCW=0Nge2JIYM6noD_jTv!220S|7HBM%A5Yn`08hx=r3DsdwuS? zg}p%KIjLcci4Ay_1`0>&cJ$oG<(*NAI~v0%KY4^lpQbs+^xg8WA#8H5UEao(KMgRM zrbQOiPEM#!gSm%2g~L6G#ls?Zc|vJ)#{LqM`jkg3Po46|<ix`seB^53XsA%OZHv;ih#*s8Y&I^rCGo5mzc32e) zYv&{>FCDpqPpJe@>B6b3UZK~6MWZ?p`XcJF-)HUWje_V2#CO(dad1DgL}3zy=ylM5g9s1Rde01e-s$dXavDibK{6pb*L23& zjMou5&I(~t_}#vPic%4X+GXWMtSz)9ixko--v=V}PKA%b=dBU0bGv>aRDy&eLf!Bs z$1N;8>QUoM**2p)_##XK-Tz_H>*jBl*@kW#rD%+~-Hyl%a>jzz#x5LiJL=szV}@{=elLb@p*m4uzD z%W|yKM=V5w1xvRa;A1j8_f0Fag;9IOqptg=-K}1==Gf>g z$aS*T;Wv8k%JOxpmKmUm^|5|26EzjAPF9EP8s${+td6T-8Wl^!=*~puQfFritjkr4 zlAqwF4%n1Y;I*u-NR{yuc zOs;N!9?okm${EDkl1GL^XMVDTyQ@G<#W=&t&SPmrPn*vmEofJ%y$r~=Eg4|DZVqRh zVFO7S$tsvz$jF@_op#8^?LA@kJNPqmLY+?^*DUtU>t8W{DVoSOW4f5t6ZF#Ep~7KrC87yndmrzRSb7IX2_Ce+pet4d=pXH$ypOetHQoio|1@}@lw(~ z^qHlCdYEXsOl)|W?}S*bSz88Q&h!d1jcM0|O$o#~XXc_xi@Jm#kBbbd%&JYG@?sb$ zp>l*0I?RG(t<)|P27E|n$VOT6yO-t@t|oVKb~*ldbp7^uElmSL5YrF^0;fh2w#aeF z7F_!gT{n>RZCq7MrS^`>@2IrG8!l!`PLQnsn5?TynZ@vK!ZBq%#@1-L%D&QBs6`^c zU@l^J9?w`YMI=z}G)ByHKL#ri2AJzWrgq~CtaeD55bbYL-J@?#E#h#V0$Nd(9j%FL zE`qroiQlUE-ptT+6W^8M!AwiFW`8K{jgfC9pX=#wOh|M_t5RAg^1%;5;$N~95Oqzj zRPr%vU~!Sd>J6`qB zGCF;O7I7gaQNX2BWhxTs`ieW9kUZ)UWg>9hEP?OCgj3I(2UDM$R8oKDI8JiDdKzfDK-013WhzEhlC z0__#Y@)Ftp-oSvRtA7Zt0!KZnnP@~=uaR;!?OQEX0*o%qJHErz-&w-+r96{d-*;sc zOEe6r<$4%j-vLSPqx1D-m;&|!z1~Gy zw-+>lGY&snUp;?F7a1Wqecpb1FgzWczTE!OOBZpOHFwVtq{E|=TD`8q+&X)#^152{ZmaK__55e+I7i`kxEH<LdP2PwLH^lMP~YHD4m~pJdd{uWV^a zF(cA^uI8V5m*h(!m=uN&?Im0w0)@?|m9H_{*n1Z$S%Qy{itT1}9sy4!R*DF!0zEp` z4n;HxNcfY&<3Y$7dN=Ica?b~L)soIcQ)17Zf+&Q<6kiW6y84xbdZ7?<2pxw_9y4vN zp&DYdf)khP!Cpc7HK`&V7M!V8Ait_y+Sl}FDxdh+{;|{&ON`DC3KJpVvx+X2sb|6) zVtqdq8Xn+NFsn>L_#REW$EtQurrlFjJN%~c^2vM^ZQhi2u5D>*z4kqDv`+;>Heh}= z{1>Lnx`uvk^r#y>1{-0UCym?M=>#@UX5}(w`B5=viT)Y(`!D+-|6M3E<}I>n6`N$& zCNZX*30WBmUEN+xdX~*;kAR%3XXOH4GtR9t&^4u8`@3SvmG!2O`2H2k```)Wv$dg%ZfIh6Jeti;Z^(MV*<(|3DjKj_Hbp2uBZy1u?!V z@ZX@R8}lk50;po z?Aui~WlO7L(M?o=@>VC9L;HFA1=dJAIq4snC6xp!gn&Z2w9lDn`-0R?khAPfU|PbA zWgDDJ4e@?nR)#f*mIw$T`Uv3mgqmSQHLWNK+$kK~0%5<}ZK*B zT=ZAG-mILMl}Xy{QKU;BFE+ICO0?;%EcHA?*>I*;rkINAvAXd;~3O3 zuxGgXlKST3(PG3nB}?zz(f^I?TH;LG@cYZ#4Cja8m??1WlqLdX>vZFBGNHSS>lf}aQ^OK z+?OIRx(a_$Kn!7dc6N4K=-XlkAKnMa0ixS!k=q?izf3z-j>c@eEo22Su{CEglft@Y zPU@;nFn?DL&xH0&AnH1M(M%%Mp*9)E*+&%-uB&EB!Y`3XMcR|q%p^i9DosbDVsYKF z&xr%5qVWIpEZ*;YcpplcC^)QkKe1}O5C?ahQOv|mllOa}v2NLSwC^G3tAA)98{fH< zqSj5?1V{mkB;Fk~n_qV*`41FKnoygk9NQ;&i~9I>YpWQ+q_-w$Y-*c@=_Yxn6Z*{m ziEjt3fEgn64R+ElOHbO7b=tp73aLNLQCgr`qM$L!p7MJVSN3898Yk9OM!HPEnZ?&n z$)gn;(spzqQcO)jqdcn^$SPYT?mOj~>xxI(*orDAr3)ja@txIt9RD3Xs9`Kwf3xwo zJb7M11&+PbE-_ebM%A`I<|{?2BN}V$uWY3Us-+lQczKX?YX&i84Xqa!HJy$)G*ESs zNI_6^DOLCsf5bG2FBMEWVBK+H_JYZ*`u^A1LW;~3@_HmY7i%P74kCm8?gy$S)J|0q zrG8~svrNCFf(x@9`2tCsl{t0dd@-O7{oFt54~0Yas03J=J_8v66;9%M?1YYuNU^_! zdz51UZ`ZBD<9fz2|WMTD^^5V!EPrH~p@*Z;4f-)Rdx3}OT?+^t|M>WzM>E7{=P zcs4WlY$k#{Eb`g1-Fh$CL?4o^V$a_OkObRX=?|bJn*Z;#>W&StB>rr%iQ}ZwPwGD& z)qgz2>o*sFK|2$9^Y9NU+KUCvCX$pJdo6l+b%a@=A|h}cv|3-G4usIcbtjUBDemX< zKUM3 z%gl%1g1a6-3i~ko^e;5yOW280kR^V3ytpB&)euvOIC||a$C&%T?aP+L@~eW9lXwQV zw~B4*NwcMLA1ao`*!{W`*AlwqIL90=@9XSpe0^7Lp&z9*0}$%3eMRC1&0o`y9Z;7R zF#JElQ|8fli@4x9Zzy!K3)4gF3-Os?>_#MVBPI-IB_;npigEQ8VE@^U3&IM7d;na+$68VVf=fBE^aOn>Ue}I#~ zI{YFDV=NOQGW(->Am5j&5|s>|&^jJt-jIOL*k1QWKI3zZTt87!ba+lx##on%EQ&9j zN}>>yZ`{X=$viU$;qfC74Qznp{B?4+kkI1;Qtpplq|6`AawSH){H zhqbcu1bxzOM8kp6eUx0Y$HnnZdv57J$&3!nN-uS9Hf|krUzZ z8Vm3$vy>R2%|e1&Cz4D+Sy@Zh#7i%R-ngO4D0FIFHM};+Aq!Z?Y|{Q5$9l&Zsi)7A zoi7dc%Zb>YZT3O;>uFSI3zuWpDJh&<&VsOh980huKIcf!6GR0wmReQA6 zjB^h6Y;iiLip^AFki_<`fmi4q5c0V~E?Q+*3muKmHen9;hn12A#xgmw#67j>7}lF7 z-}6@oiI9R+a8(x={nX~=`3|^)2Zisq@vkJMtZWsG2r#0r*xLEQ^7g~Z(9nTPq^?-T z=bbN7ETi_Mb1@BBpZEC(RZ>I$f^+y#F2>}RJq{dMB29CHcVi^PDC>}h(&gX0^edqnzB70X6WoCA5mBoFbYDnVC{M~$Bc^@dPVA7eTva| z=pBsbOchAob*pnNB6TeJa>|3Xzm_$Al_vfa8k-y{8@f`o8zt^XVwoQ0Ll;7%6$%Cl zxLktXl`oOLVZTYl4H6!LON&i2B^QT%9?jevLDxpfi_yek(ZE>|ICekVHW%DYm? zYd>JriXA)KzfsbBqcmC&umeT+AI z_e;1umZ@KZSDk1L9n=X0Sz1RQ8ipnPCg;8GIKz8r;?WUK`zeeDw!7Ip27=)E~#o0qR967RC$^8!?y0VXkTWn8CmeI!;o)_^l7KIe zd9tg9b6C?*XkyrzXQDRTkF~3FSwHjK|4Yq%ZU3xIl=F$ySoS_?Jx{HPxXQ9(b2n{Z zV}7ekmS*%%@yKrMikmOTQ_}oU#G^lOr(-4OuvUngz6ZCX=uwuh2W!!%7sXQ`=(*+LJdEOydMeyA^fN%hI+ zS7l$c3mX^;D)+XpVs<-0)q*OhOH5TO(%a$%pcv2A3T?MohufDPXx_quTdl1c(Zwx^ zFGkd%7`m+qvBd9LJlVs)70d@dlF$nH&x=J~l!X_dP24JCT(I$G7F}x+^4~V)PII)Y z@2exm(ElI}qUX*YaM$(s+8ng3uzi!fQ7}}C>~=LBV-Xh=h+lykvnrA{`FnksdBZY* z%=`h--IgF;XcwjInbp`7AfhV;6rsCmmNTmIGEENAg;)5)!Q?(JU4Ul|APuLTu4*dr zVgt)C0;+R9V;hR-(0T&7A_F&q~EgnBzd~+Hs77Z}mOZ0l? z-kMhU%`Fy<>2%LPE$`5B>20FHfPXGfLCSBtm7@u;lo>Nye8i&h?1?Yk0G+TrCiFLE z+%q^(&zVA-#uVV;`Yvf<%3c4kKTNnp1$^J6gE`@t77U$dcQP#+dy{}(c+W<<5|^r9 zOIPA9Azg`T5!I+kx+WP*T@S1YF9Z;dhG-A7+bKoyGSHaOjXSM>xvEpXp~x_BwaTXx zEatXy$~JFy`^O~JQxMw~xOg~$!hnG`mE6l;lcGCj2}fgb=ilx6Azho^#=fz)5`zjJ z5m%UNjSG$q90l@N+sUytH>?89)3OUu-^2^pAk@)xr|QG#Oj{NABJdd|>Hy?9Ds~qG z$-r9*Lmh6gOo(m`Cw*5Ua^;8T$u2u%cGI0$)|QglC6W&FuoZJ7)qK-HMvOk6>pg1ly4y0 zG+UE3SHf9^$O;6-=!$Rnx<_1F=ziPzwoNnI7N;yxWP%hPtANatcBIOk^*W9>R^(2x zcaGh#i$}2I#o+?O(a3Mn?!)Dh6QjEm4BgToIn%I7>++Y^ zmZ`xBzA9(1lRhl5JN)^NnQ7#W;Y~ z*Mx%HH|^(OX0^yonFlsWPl~n+u;fHGwm-Vr+ zXzd#tb-$8T_WuUnROEnFn9?K;(?G?+mP%P&_O%5Ixi;L~SXBd+deo_+?BJnU^EKLA z;&FQsZ3Tjg26IkwBV(~LHtuZh5UD%sVUEu)1}y|_gK-P5wij0$=L0V=#Z9WS-x#44 zGvB1T8l`1VE(MJ-25`=zkPNzdUG*SCJMIOwBL+pAS{?v>7T}{ zhh>j`-9uL_Y7t#ZuE!)~NS&V;;n|gqL=PNQV<3{pd zYZ`+5!BPyuYZpKTtdwH?WEfz-WlI(yoFixqcYr_IW4KCZenb*HFr%r$O|kJzOHo`~ zoxtRZ3@vO(AszcjPt9!LkJbGwJRx|-^)N<{B&Po8KAMZwupKcGs+qfn!5DwfVd?j` z&n_=c2B-bu=_#fNx>Nlb z#2TCp*??LT>4s`K;c5{ovsJJtDQeBCz6I)D)vb(*RoxEtsp=N&6>W8zi5v1PdKF3VWj7!J zyg3`3UH~J#JlgJT5*vt)DU`T33J&blo^m+QjGrr|INLLjJ)Tt9H3HAjnZx5gYktn_ z;sWcr4;7|mv~$kpW&IVES-s5lT!kpNhs z&&J#_jJ{LLWZ9e%2r7CNBlr>Mj;a2SL2|BNVnaG|2Z<0< z$b`P;)?0$74P;(LW)8}8RYQXc)>?z+ZrSd@^wEF!=4^OTs|X_0T(W~9f$(1XW0(Gj z#b|-Lj&Vq%*9RB<;o#up{Qb^{ZsPs4`=O%=Uw(8*mBP-&SS~oDDq%+M+fas8`H}VA z(T98x%a2)nDgVw!kghJGIfYwdCYQp~3ei1ON!S}KoNEgDppV{BK`9|f6@-iEqTTRg zQdh#yoOuWD{_rzij_@Z5i<9rEnn6Newulm z^Ktk;5cx)A_Zy8UZg>P@dp;YDRP3;s=YRgk{{m1;0Rj{N6aWAK2mt0j23??u04*T_ z007Ma000#L0000000000006du00000Vr*q@WMy(+ZDDR{W@U49E_82gY*0%90u%rg z000080OmdhUAPL~Qh0{|02K2901E&B00000000000Ject0RRABUukY>bYEXCaCuNm X1qJ{B000620swpf008xf00000q=vxR literal 0 HcmV?d00001 diff --git a/stylized_tree_generator.py b/stylized_tree_generator.py index 446d7a1..85b8b30 100644 --- a/stylized_tree_generator.py +++ b/stylized_tree_generator.py @@ -1,7 +1,7 @@ bl_info = { "name": "Stylized Tree Generator", "author": "D4rkst3r", - "version": (1, 28, 0), + "version": (1, 29, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Tree Gen", "description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen", @@ -1405,6 +1405,21 @@ def make_leaves(context, tree_obj, card_obj, name=None, card_coll=None, **overri # Spielfertiger Export: verschmolzener Stand-Baum # --------------------------------------------------------------------------- + +# LOD-Stufen fuer den Stand-Baum. Die Blatt-Reduktion laeuft ueber die DICHTE, +# nicht ueber Decimate: eine Card ist ein Billboard aus zwei Dreiecken - die +# laesst sich nicht sinnvoll vereinfachen, man muss weniger davon setzen. Damit +# die Krone dabei nicht ausduennt, werden die verbleibenden Cards groesser. +# LOD3 ist der Impostor-Ersatz: wenige, dafuer sehr grosse Cards. +# +# Dichte Card-Groesse Rinden-Decimate +LOD_STUFEN = ( + (1.00, 1.00, 1.00), # LOD0 - wie der Stand-Baum + (0.60, 1.15, 0.70), # LOD1 + (0.30, 1.45, 0.45), # LOD2 + (0.12, 2.00, 0.25), # LOD3 +) + # Slot 0 = Rinde, Slot 1 = Blaetter. Die Reihenfolge ist Teil des Vertrags - # die UE-Skripte mappen ueber den Slot-Namen, aber der Index entscheidet, welche # Faces welches Material bekommen. @@ -1464,7 +1479,7 @@ def _sphere_normals(ob, slot_index, center=None): return center -def _merge_for_export(context, tree_ob, leaf_ob, name): +def _merge_for_export(context, tree_ob, leaf_ob, name, bark_decimate=1.0): """Baut EINE Export-Kopie aus Stamm + Blattebene mit zwei Material-Slots. Die getrennten Objekte im Blend bleiben unangetastet - das ist die @@ -1486,6 +1501,22 @@ def _merge_for_export(context, tree_ob, leaf_ob, name): return ob stamm = _kopie(tree_ob) + + # Rinde VOR dem Verschmelzen vereinfachen. Danach ginge es nicht mehr: ein + # Decimate ueber das fertige Mesh wuerde auch die Blatt-Cards einschmelzen, + # und eine halbe Card ist keine Card mehr. + if bark_decimate < 0.999 and len(stamm.data.polygons) > 8: + dec = stamm.modifiers.new("LODdec", 'DECIMATE') + dec.ratio = max(0.02, bark_decimate) + dg = context.evaluated_depsgraph_get() + neu = bpy.data.meshes.new_from_object( + stamm.evaluated_get(dg), preserve_all_data_layers=True, depsgraph=dg) + alt = stamm.data + stamm.modifiers.remove(dec) + stamm.data = neu + if alt.users == 0: + bpy.data.meshes.remove(alt) + blatt = _kopie(leaf_ob) if leaf_ob is not None else None rinde = _get_or_create_mat(TREE_MATERIALS[0], (0.29, 0.21, 0.14, 1.0)) @@ -1837,6 +1868,14 @@ class TreeGenSettings(PropertyGroup): name="Export-Ordner", default="", subtype='DIR_PATH', description="Zielordner fuer die spielfertigen FBX", ) + generate_lods: BoolProperty( + name="LOD-Kette erzeugen", default=False, + description=("Exportiert LOD0-LOD3 als LOD-Gruppe (Empty mit " + "fbx_type=LodGroup) wie in der Fels-Pipeline. Die Blaetter " + "werden je Stufe duenner gestreut und dafuer groesser - " + "eine Card aus zwei Dreiecken laesst sich nicht vereinfachen"), + ) + sphere_normals: BoolProperty( name="Kugel-Normalen auf Blaetter", default=True, description=("Setzt die Blatt-Normalen radial vom Kronen-Schwerpunkt nach " @@ -1940,7 +1979,7 @@ def _origin_bottom_center(ob): ob.matrix_basis = Matrix.Identity(4) ob.matrix_parent_inverse = Matrix.Identity(4) if not me.vertices: - return + return Matrix.Identity(4) xs = [v.co.x for v in me.vertices] ys = [v.co.y for v in me.vertices] zs = [v.co.z for v in me.vertices] @@ -1950,6 +1989,7 @@ def _origin_bottom_center(ob): -min(zs), )) me.transform(versatz) + return versatz @@ -1997,25 +2037,78 @@ class TREEGEN_OT_export_game(Operator): umbenannt.append((quelle, quelle.name)) quelle.name = quelle.name + ".__src" - merge, aufraeumen = _merge_for_export( - context, baum, blatt, umbenannt[0][1]) + basis_name = umbenannt[0][1] + stufen = LOD_STUFEN if s.generate_lods else LOD_STUFEN[:1] + gruppe = None + lods = [] - # Kugel-Normalen auf die Blatt-Faces (Slot 1). - mitte = None - if s.sphere_normals and len(merge.data.materials) > 1: - mitte = _sphere_normals(merge, 1) + for i, (dichte, groesse, dec) in enumerate(stufen): + lod_name = ("%s_LOD%d" % (basis_name, i)) if s.generate_lods \ + else basis_name - # Ursprung: Bbox-Unterseite Mitte, wie in der Fels-Pipeline. - _origin_bottom_center(merge) + # Blatt-Ebene je Stufe NEU streuen statt sie zu dezimieren: + # eine Card besteht aus zwei Dreiecken, die laesst sich nicht + # vereinfachen - man muss weniger davon setzen. Die + # verbleibenden werden groesser, damit die Krone nicht + # ausduennt. + lod_blatt = blatt + if blatt is not None and s.generate_lods and i > 0: + lod_blatt = make_leaves( + context, baum, s.leaf_card, + name="%s__leaflod%d" % (basis_name, i), + card_coll=s.leaf_cards, + Density=s.leaf_density * dichte, + Size=s.leaf_size * groesse, + **{"Size Var": s.leaf_size_var, + "Max Radius": s.leaf_max_radius, + "Tilt": s.leaf_tilt, "Seed": s.seed}) + context.view_layer.update() + aufraeumen.append(lod_blatt.name) + + merge, gemacht = _merge_for_export( + context, baum, lod_blatt, lod_name, bark_decimate=dec) + aufraeumen.extend(gemacht) + + # Kugel-Normalen auf JEDER Stufe - LOD1..3 haben eine eigene + # Card-Verteilung, die Normalen muessen dazu passen. + mitte = None + if s.sphere_normals and len(merge.data.materials) > 1: + mitte = _sphere_normals(merge, 1) + if mitte is not None: + print("[TreeGen] %s: Kugel-Normalen um (%.2f, %.2f, %.2f)" + % (merge.name, mitte.x, mitte.y, mitte.z)) + lods.append(merge) + + # Ursprung aus LOD0 - Unreal platziert nach LOD0. Alle Stufen um + # denselben Betrag verschieben, damit sie deckungsgleich bleiben. + versatz = _origin_bottom_center(lods[0]) + for weitere in lods[1:]: + weitere.data.transform(versatz) + weitere.matrix_basis = Matrix.Identity(4) + weitere.matrix_parent_inverse = Matrix.Identity(4) _deselect(context) - merge.select_set(True) - context.view_layer.objects.active = merge + if s.generate_lods: + gruppe = bpy.data.objects.new(basis_name + "_LODGroup", None) + gruppe.empty_display_type = 'PLAIN_AXES' + gruppe["fbx_type"] = "LodGroup" # von Unreal ausgewertet + context.scene.collection.objects.link(gruppe) + aufraeumen.append(gruppe.name) + for lod in lods: + lod.parent = gruppe + lod.matrix_parent_inverse = Matrix.Identity(4) + lod.select_set(True) + gruppe.select_set(True) + context.view_layer.objects.active = gruppe + ziel = os.path.join(verzeichnis, gruppe.name + ".fbx") + else: + lods[0].select_set(True) + context.view_layer.objects.active = lods[0] bpy.ops.export_scene.fbx( filepath=ziel, use_selection=True, - object_types={'MESH'}, + object_types={'MESH', 'EMPTY'}, # EMPTY = die LodGroup use_mesh_modifiers=False, # ist bereits ausgewertet mesh_smooth_type='OFF', # OFF = Custom Normals bleiben use_custom_props=True, @@ -2026,9 +2119,6 @@ class TREEGEN_OT_export_game(Operator): apply_scale_options='FBX_SCALE_ALL', ) geschrieben += 1 - if mitte is not None: - print("[TreeGen] %s: Kugel-Normalen um (%.2f, %.2f, %.2f)" - % (merge.name, mitte.x, mitte.y, mitte.z)) except Exception as exc: # ein Baum darf den Rest nicht killen fehler += 1 print("[TreeGen] Export '%s' fehlgeschlagen: %s" % (baum.name, exc)) @@ -2285,7 +2375,16 @@ class TREEGEN_OT_leaves(Operator): msg = ("Blattwerk fuer %d Baum/Baeume: %d Stamm + %d Blatt = %d Tris" % (len(trees), total_tree, total_leaf, total)) limit = s.leaf_budget * max(len(trees), 1) - if s.leaf_budget and total > limit: + if total_leaf == 0: + # Passiert regulaer beim Kaktus (kein Ast duenner als Max Radius) - + # aber auch, wenn Max Radius fuer den Baum zu klein steht. Beides + # sieht in der Szene gleich aus: eine leere _Leaf-Ebene. Ohne + # Hinweis sucht man den Fehler im Material. + self.report({'WARNING'}, + "Keine Blaetter gestreut - kein Ast ist duenner als " + "Max Radius (%.3f m). Beim Kaktus ist das richtig, " + "sonst Max Radius erhoehen." % s.leaf_max_radius) + elif s.leaf_budget and total > limit: self.report({'WARNING'}, msg + " - UEBER Budget (%d). Dichte senken." % limit) else: self.report({'INFO'}, msg) @@ -2495,6 +2594,26 @@ class VIEW3D_PT_tree_layers(_TreeSub): layout.prop(s, "make_frucht") +class VIEW3D_PT_tree_pieces(_TreeSub): + """Eigenes Unter-Panel: das Export-Panel war mit Stand-Baum, LODs und + Faell-Teilen ueberladen - drei Themen in einem Kasten.""" + bl_label = "5 - Faell-Teile" + + def draw(self, context): + layout = self.layout + s = context.scene.tree_gen_settings + layout.prop(s, "piece_trunk_segments") + layout.prop(s, "piece_ucx") + row = layout.row() + row.active = s.piece_ucx + row.prop(s, "piece_ucx_verts") + layout.prop(s, "piece_export_combined") + layout.prop(s, "piece_keep") + layout.separator() + layout.operator("treegen.export_pieces", icon='MOD_BUILD') + layout.label(text="Pivot je Teil = Massenmittelpunkt", icon='INFO') + + class VIEW3D_PT_tree_export(_TreeSub): bl_label = "4 - Export" @@ -2507,19 +2626,10 @@ class VIEW3D_PT_tree_export(_TreeSub): row.prop(s, "generate_lightmap_uv") layout.separator() layout.prop(s, "sphere_normals") + layout.prop(s, "generate_lods") layout.prop(s, "export_dir") layout.operator("treegen.export_game", icon='EXPORT') - layout.separator() - box = layout.box() - box.label(text="Faell-Teile", icon='MOD_BUILD') - box.prop(s, "piece_trunk_segments") - box.prop(s, "piece_ucx") - row = box.row() - row.active = s.piece_ucx - row.prop(s, "piece_ucx_verts") - box.prop(s, "piece_export_combined") - box.prop(s, "piece_keep") - box.operator("treegen.export_pieces", icon='MOD_BUILD') + layout.label(text="Slot 0 = %s, Slot 1 = %s" % (TREE_MATERIALS[0], TREE_MATERIALS[1]), icon='MATERIAL') @@ -2537,6 +2647,7 @@ classes = ( VIEW3D_PT_tree_leaves, VIEW3D_PT_tree_layers, VIEW3D_PT_tree_export, + VIEW3D_PT_tree_pieces, ) diff --git a/tests/test_baum_lod.py b/tests/test_baum_lod.py new file mode 100644 index 0000000..f9fbe3f --- /dev/null +++ b/tests/test_baum_lod.py @@ -0,0 +1,182 @@ +"""Punkt 5: LOD-Kette fuer den Stand-Baum. + +Die Blatt-Reduktion laeuft ueber die DICHTE, nicht ueber Decimate: eine Card +besteht aus zwei Dreiecken und laesst sich nicht vereinfachen - man muss weniger +davon setzen und die verbleibenden groesser machen. + +Gemessen wird am reimportierten FBX, und die Kugel-Normalen werden auf JEDER +Stufe geprueft: LOD1-3 haben eine eigene Card-Verteilung, die Normalen muessen +dazu passen. Nur LOD0 zu pruefen wuerde genau das verfehlen. + +Aufruf: blender --background --factory-startup --python tests/test_baum_lod.py +""" +import os +import sys +import tempfile +import importlib.util + +import bpy +from mathutils import Vector + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +spec = importlib.util.spec_from_file_location( + "tg", os.path.join(ROOT, "stylized_tree_generator.py")) +tg = importlib.util.module_from_spec(spec) +sys.modules["tg"] = tg +spec.loader.exec_module(tg) +tg.register() + +fails = [] +OUT = tempfile.mkdtemp() + +for o in list(bpy.data.objects): + bpy.data.objects.remove(o, do_unlink=True) + +me = bpy.data.meshes.new("Leaf_Card") +me.from_pydata([(-0.5, 0, 0), (0, 0, 0.7), (0.5, 0, 0), (0, 0, -0.1)], + [], [(0, 1, 2), (0, 2, 3)]) +me.update() +card = bpy.data.objects.new("Leaf_Card", me) +bpy.context.scene.collection.objects.link(card) + +s = bpy.context.scene.tree_gen_settings +s.preset = 'baum' +s.use_growth = False +s.apply_modifier = True +s.art_name = "Eiche" +s.variant = 1 +s.export_dir = OUT +s.sphere_normals = True +s.generate_lods = True + +bpy.ops.object.treegen_create() +baum = [o for o in bpy.context.scene.objects + if o.type == 'MESH' and o.name.startswith("Eiche_01") + and not o.name.endswith(("_Leaf", "_Frucht"))][0] +for o in bpy.context.scene.objects: + o.select_set(o is baum) +bpy.context.view_layer.objects.active = baum +s.leaf_card = card +bpy.ops.object.treegen_leaves() + +for o in bpy.context.scene.objects: + o.select_set(o is baum) +bpy.context.view_layer.objects.active = baum +namen_vorher = sorted(o.name for o in bpy.context.scene.objects) + +bpy.ops.treegen.export_game() + +if sorted(o.name for o in bpy.context.scene.objects) != namen_vorher: + fehlt = [n for n in namen_vorher + if n not in [o.name for o in bpy.context.scene.objects]] + neu = [o.name for o in bpy.context.scene.objects if o.name not in namen_vorher] + fails.append("Szene veraendert - fehlt: %s, neu: %s" % (fehlt, neu)) + +dateien = sorted(f for f in os.listdir(OUT) if f.lower().endswith(".fbx")) +lodfile = [f for f in dateien if "_LODGroup" in f] +print("Export: %s" % dateien) +if not lodfile: + print("ERGEBNIS: FEHLER: keine _LODGroup-Datei") + sys.exit(1) + +bpy.ops.wm.read_factory_settings(use_empty=True) +bpy.ops.import_scene.fbx(filepath=os.path.join(OUT, lodfile[0])) + +meshes = [o for o in bpy.context.scene.objects if o.type == 'MESH'] +empties = [o for o in bpy.context.scene.objects if o.type == 'EMPTY'] +stufen = sorted(meshes, key=lambda o: o.name) +print("") +print("%-24s %7s %7s %7s %9s %s" + % ("Stufe", "Rinde", "Blatt", "Tris", "Radial", "Slots")) + +if len(stufen) != len(tg.LOD_STUFEN): + fails.append("%d LOD-Meshes, erwartet %d" % (len(stufen), len(tg.LOD_STUFEN))) +if not empties: + fails.append("Kein LodGroup-Empty im FBX - Unreal erkennt die Kette nicht") + +blatt_zahlen = [] +rinde_zahlen = [] +for ob in stufen: + m = ob.data + rinde = sum(1 for p in m.polygons if p.material_index == 0) + laub = sum(1 for p in m.polygons if p.material_index == 1) + tris = sum(len(p.vertices) - 2 for p in m.polygons) + blatt_zahlen.append(laub) + rinde_zahlen.append(rinde) + + # Kugel-Normalen auf DIESER Stufe nachmessen. + quote = 0.0 + blatt_polys = [p for p in m.polygons if p.material_index == 1] + if blatt_polys: + pts = [m.vertices[vi].co for p in blatt_polys for vi in p.vertices] + mitte = Vector((sum(p.x for p in pts) / len(pts), + sum(p.y for p in pts) / len(pts), + sum(p.z for p in pts) / len(pts))) + if hasattr(m, "calc_normals_split"): + m.calc_normals_split() + treffer = gesamt = 0 + for p in blatt_polys: + for li in p.loop_indices: + n = Vector(m.loops[li].normal) + radial = m.vertices[m.loops[li].vertex_index].co - mitte + if n.length_squared < 1e-9 or radial.length_squared < 1e-9: + continue + gesamt += 1 + if n.normalized().dot(radial.normalized()) > 0.8: + treffer += 1 + quote = 100.0 * treffer / gesamt if gesamt else 0.0 + + print("%-24s %7d %7d %7d %8.0f%% %s" + % (ob.name, rinde, laub, tris, quote, + [x.name if x else "-" for x in m.materials])) + + if len(m.materials) != 2: + fails.append("%s: %d Slots, erwartet 2" % (ob.name, len(m.materials))) + if blatt_polys and quote < 90.0: + fails.append("%s: nur %.0f%% der Blatt-Normalen radial - die " + "Kugel-Normalen fehlen auf dieser Stufe" % (ob.name, quote)) + if not blatt_polys: + fails.append("%s: keine Blatt-Faces" % ob.name) + +# --- Die Ketten-Eigenschaft: es muss nach oben duenner werden ------------- +print("") +print("Blatt-Faces je Stufe: %s" % blatt_zahlen) +print("Rinden-Faces je Stufe: %s" % rinde_zahlen) +for i in range(1, len(blatt_zahlen)): + if blatt_zahlen[i] >= blatt_zahlen[i - 1]: + fails.append("LOD%d hat %d Blatt-Faces, LOD%d hatte %d - die Kette wird " + "nicht duenner" % (i, blatt_zahlen[i], i - 1, + blatt_zahlen[i - 1])) +for i in range(1, len(rinde_zahlen)): + if rinde_zahlen[i] > rinde_zahlen[i - 1]: + fails.append("LOD%d hat mehr Rinden-Faces als LOD%d" % (i, i - 1)) + +# LOD3 muss deutlich guenstiger sein als LOD0, sonst lohnt die Kette nicht. +if blatt_zahlen and blatt_zahlen[0] > 0: + anteil = 100.0 * blatt_zahlen[-1] / blatt_zahlen[0] + print("LOD3 traegt %.0f%% der Cards von LOD0" % anteil) + if anteil > 30.0: + fails.append("LOD3 hat noch %.0f%% der Cards - als Impostor-Ersatz zu " + "teuer" % anteil) + +# --- Alle Stufen deckungsgleich am Ursprung ------------------------------ +for ob in stufen: + co = [ob.matrix_world @ v.co for v in ob.data.vertices] + lo = [min(c[i] for c in co) for i in range(3)] + hi = [max(c[i] for c in co) for i in range(3)] + if abs((lo[0] + hi[0]) * 0.5) > 0.25 or abs((lo[1] + hi[1]) * 0.5) > 0.25: + fails.append("%s: Bbox nicht zentriert (%.2f/%.2f)" + % (ob.name, (lo[0] + hi[0]) * 0.5, (lo[1] + hi[1]) * 0.5)) + # Die Stufen muessen auf derselben Hoehe stehen - sonst springt der Baum + # beim LOD-Wechsel. + if abs(lo[2]) > 0.15: + fails.append("%s: steht %.3f m ueber/unter dem Ursprung - der Baum " + "wuerde beim LOD-Wechsel springen" % (ob.name, lo[2])) + +print("") +if fails: + print("ERGEBNIS: %d FEHLER" % len(fails)) + for x in fails: + print(" - " + x) + sys.exit(1) +print("ERGEBNIS: ALLE CHECKS OK") diff --git a/tests/test_panels.py b/tests/test_panels.py index ec514c0..1db6f33 100644 --- a/tests/test_panels.py +++ b/tests/test_panels.py @@ -28,7 +28,7 @@ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ADDONS = { "stylized_rock_generator.py": ("VIEW3D_PT_rock_generator", 9, "rock_gen_settings", set()), - "stylized_tree_generator.py": ("VIEW3D_PT_tree_generator", 4, + "stylized_tree_generator.py": ("VIEW3D_PT_tree_generator", 5, "tree_gen_settings", set()), "stylized_grass_generator.py": ("VIEW3D_PT_grass_generator", 4, "grass_gen_settings", set()),