From cec997d1d2377f0c1beae9cb272fe5bd3e4741b0 Mon Sep 17 00:00:00 2001 From: Razvalyaev Date: Sun, 9 Aug 2026 10:19:12 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BF=D0=B8=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=BF=D0=BE=D0=B4=20=D0=B1=D1=83?= =?UTF-8?q?=D0=B4=D1=83=D1=89=D0=B8=D0=B9=20=D0=B0=D0=BD=D0=B0=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +скрипт для конвертации падов под CNC --- .gitignore | 7 +- PCB/OptoTest.PcbDoc | Bin 3085312 -> 3190272 bytes PCB/OptoTest.SchDoc | Bin 127488 -> 128000 bytes PCB/Scripts/RivetViaConverter/README.md | 62 ++ .../RivetViaConverter.PrjScr | 8 + .../RivetViaConverter/RivetViaConverter.pas | 843 ++++++++++++++++++ 6 files changed, 916 insertions(+), 4 deletions(-) create mode 100644 PCB/Scripts/RivetViaConverter/README.md create mode 100644 PCB/Scripts/RivetViaConverter/RivetViaConverter.PrjScr create mode 100644 PCB/Scripts/RivetViaConverter/RivetViaConverter.pas diff --git a/.gitignore b/.gitignore index 06fddbc..e9c978b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /tests/ -/PCB/Project Logs*/ -/*/__Previews -/*/History -/*/Project Logs*/ +__Previews/ +History +Project Logs*/ diff --git a/PCB/OptoTest.PcbDoc b/PCB/OptoTest.PcbDoc index 48aa9965ecba8e734458bfa772b1e263ae1aec82..54fdcad658ca86cfacc5af22c4dae7fd0a1a1347 100644 GIT binary patch delta 56607 zcmeFacU)9Q_dkB`0=uxg6njOmR}he5Ls@$lTP(4lF?J)C*Z_Bl#*)MYV`LI5YC<$d zMWe1QioGk=*u|*PBu25sit;_@+#enu`Syw z+b-^uQ)5$WJ3pRb*GG*>%0AxTgHn$LC|6H^~Fzkh$Za+&w>otlQdk^0NZdo)wTG=peX3 zaEIUlK@Y)Te^$WrgSI7JLaQ!8+b=`70>K@O;s)WWNbchqApb5FL*$84_aRs8@8elI zARDfpL&$~j0>VoOc@ShMx3Q}%^;1-}WG9ol83R()VP50>9p^=ecAyJeG3PZyp>ePyivQHT3C0pcL_Jn0# zHw_KLLTiWD4v7j64xxXEZQ8eQZRs4`JsL7f=rs#bVJ z?Zl?tn|5s9+0waX`*u)1BsjEI?NBhHb25|;362bljHnZp*rR>>wr#t#=?s#n5NWq; z+7&w?7|jz>2a>`aY0!eGkjSXW+Tn@KJBIY>+wbmP9G#=Jjzu&A}h>q%J`i=#;M zPWjB(Zmri#S&k5G*Lk(|hzJd>9V*JM_X;W;8Xc9`!qTZl>!#hyhef|jeNQ>wVbKv< zPAxHiy;rz*t>_RfDO_Ay?-k(|9vx{fvcc;^UFv49_suu~cp}(GZuaUbdyIj(>o>cs zdogdC9$N0bEnZ{XxIpYJC}h+QRqupL{ zZW~T{Et9Q{LlfK9itbn|v2A#C$FRh<5z!qXB{I5wZ09~5yCt@bif&vxv2E?>#u15a z>qIvWPiz|+65TioqR{BZkr0JNH-^HYwW1qC>Co`#_HCLrfs2UfZk<3;Xk>J!u6;Un zwshn8ja@4DcoFS5*d6g8I2fT)ek571YQX6>ngjaxUU;T&IEI0eC-@W#F zh?gh4J{A}6dbLN1^0F9t#H)~K_sC0#&-Q!y%d0d!ABeC6UTsx9(+_xkt1s{ZRv8_v zA-LL9fVkqE0>)w$3&WKef;R*o2)+>fAQXU55JDjc9&_3n%SZdeRT&5Y5CS2Tg-{Mc zc?hVpGQ^c2RD)0jLRARWA#{b%dCp2>DY@~S1IF@l{W*WZbq(Pq8!Pu}3z_X8w1*H2 zp#y}D5IR9XrClI?2f}0s-5_*_Fa^T95PCxB1)&dwR&e);gSan*eh`L2=r3lzwyYclq}Nf8f&CJ@oM5V4#pl-pI3s0HjPdg zrSXwCN=H6{QR-oWvY(0xPrZhEQ!;0Zdr!T-kmuT=N3F75&h^T36ETI2&F#%~#!Ut} zBGqV|>=jlgs!nj7u+T6O?P1(tZ)7q)k>ye1c%Cu}ng~A+V-c~++gQ+}R&aP&gnh5K zF<$2pQY$z#++NSmSj}LlT_-pq64tz7#f)FM%T4TZY2y*IC#)2~QE-P8^#YAuMCsbb zGWM2Ljs10=;k6@!!@@!$92o*K>Wh-ajfL&wYZ#}xi&j;PW9*|tjWdnnWj*6Fd7*e( z-`L(G2}}_{W^iezePpz8zRs<4`&fJ77^7L1L-FQ&Ewz#Hx~oC#_Zk^T%lXnqHhDGe zUQ_3^lqqEE=0-O+k6K{sSzjG$2~(spq+K< zgbC}X#<~V*bYv*p5i>tER`d)Fj|h&8tW(Q=`%_~R9KIW27kv0H>`bPbfUn@_+sEY0X98{Y%QC)?r*CUz__78P@s8qM~=CB^|B;`DuE zaS^-AXb_1PjK;9u-T$C1=ciY+xi!D~ zFB(ZEBW-AWH|l^quT65N!P#$YY1vc8LZaawqrZq-W-Q}3%`)%Cf%26tA0LpcxnsJX zQ@WxgvP7K@y@e_t|6ldC4sxY>^7@YdRb`04?h#%08+`(1FIoFPsiR?o<4JK$<3gk2Eu?t)=2 zT<>$j2jTkA?8SysqV^i&*3`8|_kZXtoHrt6gV9|C?KT<%{#&codsW?Sg*hP>BJ8H(Q@G?uD? z_dr}{^71?)E9j<2%zQ&fi`YxX>LO&hvBGQDnd)N12BTga-vmZWJ8g8YqUhcGpz!~R z-e(()RRi8k&>)h3Gd2=wKN`(%Vpx77G#rPbLa@n*NAjEX{&x*;V+XakI4WxOK{x*o z)YkmJY4XDL#!^La1}fa9F5A!CM2F+XveaL6vkG|SX61C_I_PE=9WKFb=E${Dk5l%I zZ@T92qi)G_e$Es)!%0ciLNu8ArN_(vP7}_Q5V$uYSw)wvN+r4Gr<+`Fq5l;t*PEY% z*}v0EmnV&$|3Zt_ z9%T(jRpk@vUXsRdHojq%GyDT5#}W>T4CNAU|4TiN1)SQJc>F)KZP9VC&@sPH>rR{c zn`MD9uR+5PA8o=Fyw|@l36EiX^7NxIyagI^IpxqB4FZprA?SDn50wlaz~jcEe#4E6 zi@x)7cH6YspBBpgAuLkxK=jZwqpANdG{anJUsWMn@5zz>6P4P?pr>L*_w)7J{-f^W zSKtvZ_THM}bG?x2#jfd{dZ2p6x=#e)-_^yL>0v`(Atn7^s8+1Q}1IJpJ-m*2m&c*>W<77TmGfMYX-z7! zBvbm~WcF*vCBLBBcEPfk^yTnb!w+7FnH!SSb6O=NiO zol0fB0(Uo)5=w4OPgzkZVr%4-LK?bnALtC59 zN9EPAX6bc7a}UHAe>k>&-^C_hdOx`)3=^(v_W)e=9J{x0 z)aqjOW#e4G1NBDb{<3(4<+FEZeEsYCa-n!LrK})fxNIE$&-vv=);Z(2|B!DK9_L=C zgyvn^R%2h>F>|_+_RFcIZ!o>_+@z-A$9wMXsJxpw-aLUtgE6arL3r zA=jLS!{5|8XOk#}&?Nu4Q0oF{M8(Tr zYPZL}uVf$ahcQD(R#b}MYWUw20ZSD7>vb&!-mPVw`0GuXyY3h#`X)7OUoAdvL+knJ zrc3UB8pPH8#!!)W4?OIi;+wnwBoMO#yqzQd2H(c09OJk*DrtJ}pOjE7CQ<7CKQ_Qq z+%&MWsocwrR#9vJKR;~vOI;F?9p&s2y%hEN4IsMkY0UHI!vrM$j|%M=K+gax2H zP4v>4-mSPAQZgW{fv^_BPY~8YKt+cjwnNw`F6m6g>g^t;m4R#?T+4b9df6Bt zvR8w7`#b=b#w*i}I@VJxc=!gRjsE+MCYMFaNB<B%oqb>+^pThL{@f+Y(4)Vz&zv zI}))ZCPJ*3Dbo||jQX8ZsXVg4L}Yw+6G3OXHai>HlMNKPgkufLP2S&wZACsqjqK~Pgo5-qe z()%v0Z{`6l2pt`-o6c=8|2?IcS9^< zYnZC|KHR=THuV}WW1Q1_lCV^UCVx-?8hWCDDOe1vU@{AH1=A9m06d^)RRpHl6~GYA z)54MJJcS@NAhjT9d1WfkDhWda=l0UtcDdIt69gnw_Ejf3O?Wl2x@p1nM3PDxpX`vr zM3Pn#LD%?XEdkbvvSL+1n7Q4{d?&YQUQGwFC|eM!4a-%EF|$N)&nB@rdn zOq47uVhe%7y3dc&gojuhFT|<53_$FZ>TC&DSzb&y_0`1$aw_|+y=;^JIP!=5bN8JT zi;zO5K+(IfNl#fWA= zi$Mc3io!^2vU#-(DgTgzWmJiLWmp@;PC2N*!oZQxI-PB{|;SIdy{4>?#ymB?3y zwLxqL*2{GwvlI;Dlu|HE<9m#zkpVH@LCRv5(Uh|HgI7}30+@xv(zQT*#eq~HcGFNz z4c7o!Sl}PBaGXMEVJQs*(EZYB35QkG_7cjlI$9d+tESK>bj~r7JdR-JRDD5|M@^W6 z@dTTQD`To)kMTG8$o4M-Ob>P9V0lyII@1rb=SjL2196ZyA~5K2v8JbS5Z!d7)7g_FrigtO=EQv$UB!SD{>-W=x0U%kIy4ak)C2=Yf}x86Kyhz z@Me6qzp1GPyxb%-F-6cGmhz~F-m76zv*o=%o)V!=O=X?3AW8nQ-Xd8F{EM=n@ER

+{_gb6E!VAv5Xcvj;>(k^4e&c13Bnf(lQwkT%J1lMNeqYRp zF;y(#(5WU%7+>!3RLqJoRZeXM7MxrCn0$ZsS^2XXCq#$lFzU{A-6|*FT}z3ONr|R4 zIh!aEGDX8?rm{{IqT9Rb#NA?e1D6WVY+WktZZKEo?5hn-{!V-_`^-vNR>efV>^mED z^2TB*a@DQtG0vaev(izi)w12PD)vv=DE74RAtf8%{FQ%0YW*pf`)sTH@w^;4`r!`B zhF*wjY>Fu1kW$}lC5+d$d4Lx4&zFrZbAAEFY^PqX-2WQY0dJy`ym7L}6{-7LH!YKzeeVunhRwZ7OgA)C|`Ax_zq5Gt&2jwGEE%NNWXXLuS zhr1?XW{JUVl|+>AzFNywk`vL5dwy$}BFR2oPK3^??EA}(eezdvVKT;fS0j;EWnVlh zWLn?q^@-{^BC)tBNKW2lhFme{HcjrW|cNDDv7sPsF!|p5RoBf9TawDX#)j3d$+b4a+@l zW2*X^!TZU~bgA5>m&(v^%r!MxI-0oeb=hjN^HlT~X$^rCzaVlq<(m{Z>mTf@BCE5X zMgpu2LmQeRlty9uuLY%0`zJ;!vjzkv#F!!=TCGGkl&F-25<6gI62oEaL*xq!5kyTP zQr5YbxVm`?x8@|gqa{U;l_;ncq4$*N8znlXL>{dvubvXEZv#jt%C@Dbs}jvrqFqY# zREcV~0}+#WA0lzNJ(%P=JZ0Z#b6CdMBA*dE+ME1ENJm(LFTVpzFv^I}yQx>ccoGT^ z!kp-~8Do^LEOv9Y;l<4Lcu-0#?qUKbA*xpeNj+3U1$>*vek;#|Ws${E3#rCKR#*ac zP$EjE+*l%)AHG3eyFW##uRaqaMVPHBRLZCdoLf{`4CQFDa70I}eg_0F#dmtZkMf492V~B1*r;1Kli3Zj#PQBxh*n+MPFTs^ zhofY!3`>C7<=&>S(%{l`EQ08G1U53pV_(d~7C0}0n2dTQ-N9??%$C7g14d}ftX7tWC}UcG9+Qw|>?c{L|;%UaT=YIQ2Z+EfXr z96p7FG0t1+KwB)dl(!8xpVsS6P7ZO(QB!1$|6b#q6Vt^**Va8u#&}xa2&Xdphdhz< zS~q%4&ah!WQjRL&RED*wOvK0zrpj*JdM^`ybu!iRJ=q1$WWh}U66nT)xu$v|h7uuD ztmz0VZdwnMkLmA^^|D$GsW0IeCpTApD);K&Kvu^K)gjh(f~Chp?CAhY0S)tOFMpGt z$1at1E&rA;>gOmNDF;)w_MG*G9NaNw%Q{s&Xw^+A&u~^gb^RX3`FV9{d|A3R|ER2r zy)Jv*H-}|aEPuADR4%&isq}O0to+%SJgDWTF4=JEogY%vY+>pM(>$jeEOY4{VI3$4 zRuNh402apCOjkC%LPLn#+EqDN^uT%tx`Xx5{KDMBRE1`b$nF7p+Pn*8aEKvv7gOJb zTv|-;Kn5%YrFzmr+(C(^^)!VEQy-JJi0KW_j6e6PDxVq^raNh!BkuKv#c_Pq6*7QV z6H!vlbVzb>h%vFcsqug&^0hwe<+kG{DpHNQry+PX5hc}3ha?w=7!!+#%Y97c>~NMo zPKM*)W8`h(!#L9jS5Kw=UYyBS25&aHUQ;JQp%NdNin-OU*&V&0HALx8z;E+KKT|`) zb#lyB5ose#L8+4=EBHNA9m>bj@CyaZspBFhJo}q^If)d0jfnnz({QDh#qZ~%;+#ZEzD9)g1?sw05N(H=f}G^xoaHNakv{=LhYT|nbKmmKIZ1?mVyf;`qV#J@ z_>Dlu!`T#zhv#pS{4v_<15lKGKOa}VYRM*} z+sE4m10D{ zvZ=fXoMAGEiqp8UpG|wEN?eBuPj}H*>5cl7#Zr7e<29#Q19Tg6NTVts3KLyZUvUcf#sSIPF-LLmtWn z25u(I`eGZT5oW?URi+SD2wUNo;tvN<0y9q8-qJ}xl0tq6a#0YszYt-rAiMaBNzR0u zwtYUyGPdODb7*29++*D3NtW%!6$}J3S6p<+L;X@r?jA~Dh4CeSB+QKcJW>d=iQ}?H z5oW>}^SZd?$4;%|Qr`A$0AbcI{q0E@Eo`-biMv!aOtNgP_|9IH@sLh`Bw0KvC>R+} zyOpH15DA|>PM8(OKe7|n2w$B?SR?%7B*GfuaeX=8N~1KkUsD$aHd}SVT!D1FFkv1O zT|5dUxe%`TDA9%Rdk+&`2(P z>c9z>F{|dxTJ}jI#v}ilLWDC-1H`puruVIqJZPdN;i!Mg?z@VO5ySTf24(N&}%+dhv*WyTqMg5~F>7n@&SVzmU7^0y@(OR#us(Gvlvh%gfX zoV~kXqUA)MwFAo6Eagua(^Y+0L#mNwVt<`U`qSDpAso^q0Wb{Tz}y~**cU|$!(a;7 z+3(=Kp41nFC)8iCe!&E%0`ODX!3i!3vx|R8m@CY9wt{eG{cov4KfnEi1O2}zoY~}t zBOj+v`~;mcIGFM0We97G<7*RE7~=xbo(7Lb_|I4x?Hb`7!(HU#W{z;tmtB7|pZ^HLf#DAoMQVO#nX`(VT0W=#Ho{cFnXkIXUfw#% zQf+9ub!TTvkA1O-V%yS@Di*pQO{vzj_uP{#x#ORO4{el)@mH-XCtBL%-dNhNLZW5u z=J9jA!jvu^U0#Wk>rpJ-ElF{V=t^;X32%!1CXA#wHsP5vIjT&jAr{&CNdgYBx({d2 z5FvgRyvQ5f5FJ7KOcu<|*=*(xDh{l5vvbgo2k$jE|94>95IY7*x1 zFB+?ku7#D{X5LQ%`@q&tX=DNsdZJO_-5Pq*d_$R_#VPNYc zgf+r(zY`|HI0-`L=&4IsAp1ah!mJ?XN<+e$iFb5Yx{ysAA5S>z&^0o7hRGAs%oU{Uu0%LRB@kb! zFkwHHV0NrtDX%s_I{acH*2fJ@`}3KV64(MPU{g)R3fP2P0h^c!P!}{gzkK<_DhYT~ zq%L}XLBVhvuZvv>6|;N_b}``iN>-JzItI8x9wbaCTOCBeSRt$qGS25g$mMyEYJ}B6 z%miqVBVj8u##!LZ7^jIdW1L3BSS74ZGp>-QArs10r>RD`Z5b-h`L^ORVJj;Lbh|-) zr7>ykv6>x&uvUV_DR(yp6pnqe$- zv@)#i_u7nxdE!(?bX44m;7MxYh^4(u9_v9utMaNM8k31^%u z9FOTawN?_&{-8SHthzspw@eFp(AYYgx{vVtUpUbM@rl$gNZv2~2t6WXtlKsY z^~CMpPPlx^k{>A{+s{r3aY7KzDz=Vrj4s(;eFyNh4ldm-C2C}nrQ6DD3Gde!ZaLDR z#mt!lCt1F1-D?bD_qDV42MnELspA>i4>8o@H#~8YrSQN-FMVVOF7V#&MOB6k)1Eg# zE)6ln^t|zGemxhgfArO{UpfxAY7ZQD9#VpZA2{|b(Pd2jj4I&Ub<9@_3G>ZQ+J1&G z-~8gL6!-uuOYoG+fH`uSmc3Hh3JE&(5e;0n z?R%Dqx13a;kb+D3$0CLnX$p0bgt+K0Mp_JJJ; zt2E6;VQj_%!dzkCmv+KzQrq_@3A4#VF6Ox8XZ!h#bRk@+8exsFXA{Cq*qUPRK?(f8 zkAPcTf~DifpH3=2c$DS+t@j7m;9*#&OY^NUZ4?p> z{N^WRLHSX^wpH*j4C(QqDU=_Z@;hP4u#j+8LkaZ5{WS2qj8XWcG0LP%wA}3TN6ph~ zM_Hh+dR>JjaA98sgCc2VAQ2*YfVQ4dgjr#1gARnb!i-MC38$!p;-z5FJn5#~27RUf{IQ6F=K z8S1+l6N*#c-W8d!`Yx#v#&=C;!c`5{NqAGx7^`oq8e@K&Wyb2; zE?3BJ!%T?ZmNmla+cf9%+cuYv$+fDlVrh>u3>LY&V&BQCG`lvMrfb0Px1eXXJ7lw!v9Rj-v3 zWAW6kTODrB!1SIAy7jgab9W5UyGZ%~|4G%aEN)o#37@mi@1MPT@G#Yv@Bkp1aJ z!Ybp?H6t(Cv~>arU;NYNyiSbYdD|fmCa+!ciHp8KlSi_)FyZ{qDpH6Nm~oYivV@tj ztyPFien^)F4h8FbxznFB+;Y1M8G7WEIA`dY8vob^2Q=m1OIyIFnO&lqZXnbQ7rg2zBhcq{p{2lIy_ z+qCwj24ZaUA453%(-;MBYo_2oh7*qYs4QXIs>Or@fAJt3^DJwiWkk=V%_cn_f_eyJ z{9(yqvdwP?w4GBe0*|RR?&q0=H>pJ~Z zru9(E{qc1_tzU7l<&j;%pg#L#mBG}YZOyDo3&P%^1g!;+W)S8IW6JI%9Czd)wIJ=2 zT*7JQyM(2OkrZ=&W|RI+8ieQnT#6*phz?vGl4L&7Dw@BVWU4rS8$)mHbvZ9cgy816#=!v%)J8$Godv9B*K~3Zj!Mwt9(j0EqNDV z>A@ExEGsXZNZMP&fq__H^HPMZ?SXCIaC7du=Ekco z`L=5%sN1=G;P-8niN%EV34}Gm)&-M>xiD_IahMC^>@AK0$hhI}E(&AZ{Hd?GLMgZl zVU4i(g0MzdI_Q#bwN=;A>i{Vj+x>-a{lXturv7}lPmM}oQlFtYH`{(-35aO2c16*6 zOC@gw1CdO`5Xb)+t5_l*j#(Nr((=qplP6rzq+oEkw#EFYURI^T2^PP!a-qsYuSzK8 zQ3T;km2rGR1Hy4?i?ZIeD9@~Fi#eYy!sRuKm90YZX{trDKPocPveTMSw>6p^z4ILl zjkNeo+cO5c1ikZ!p@o5JpHTtC+^4ES?sKk?#{?7NF{2Sy#}wt`y@&@LD@c2+4oYUM zj#`B=5XMpMOc+PKGhuaBXoPvzFky98VSP0JGu3IQF*a z&(MueBmB;M!kiydy+2_tpEfXruvFHn4hl~EqwbB^YDl*HTy5Bft{;uG zBrnM&{BpBC&Q@@wW&5^{m6kP3#yt(fpe(0AeR}ALQD11QRE{9*r@qh@KU-aSNK)Tt zyV(K>Yww$1C=XfqK5Dh#r&T@}b+2LZ-)xrC<8ziE2}p`-dY&HD_@#Ax=Oct!$;R9r zg!!d;*~*QCnMC%^HA;CT`FPm3%WJH%SboK5;}ISPRl;#E_T62O`%)k@gZOoF?DNpe?FBLYDI{0kglBnMdFGP5#9 zzWHq|)@6+^>!p~!#%@Xo%y>y8Y@@y+0nC|TOt}e! z+-JPxeVmIjo-;~=J8X+=OC2XSBJ()i#?;`d||W)gu94iILN*)KK{ z=0>D^wu&$}GTUQ?f)y)(|A__Eali_A9B>6Z4wwLU0~6$K;s$Uxas#=W*#gQqK>K%@ zsNp52XdaCNlp7E~xhP>C2Z6?t3PuZH z{hEL}RagO=kSkylGXd@bCdggH4d8LW4Wur1hYC9{DiB}Al4ObBaZv>RX{v}$a7y?z z`Q0RRa>E}RkOrJjXm6}B&N68K^gXr?A6Ozf-y|%$hQ^^xOwS*5U&9CwZ|&Jvb&P8x z-X1~^mOP+sDIXH%0UhJMo-hw+Yv7XGl)xi8vsp>Sajj0tn9%x!c|>Oy0zyb~;Q54_ zHNc(cM3S%*$zL~t`JG5=)>cV^wXM`2%#zK`EZN*llFiK|+1yN$&8?9HOWxgaYpf-C zV`s7?@aDydWN;DU)r*zz>c*Au>c=E_b!3vf{Bk3-R*RZ>~{zXOYf z6H6k*Eb*CP$f$ec@NStETmHRYS`Wv2I$`&*-n6^&N8a`-_~6njvM%qo@ZHchW*)sQ z@!c}8_A$b6H?`7}N}$N41iszIoh(a(l5fYhNzudU9zJx`z2Ta^piz&~Zz)H!4`F@ev-!qjGc>2XWuoLF_nJsP* z=J}f$;ZK&*=J)cRgthrUdGRosonJO!uZ_JA4 zBeUZ9%B*<#w5hDL2@kBSL{FX;gTqWzI|=s&MMYX7Rs+XA?2Rk!PI}m26n^(iMXd1PJswwy?(l~NPzYpr5|a|Z+9X#i;b&?l!K(z50JHayb2gNtNCV2X) z+udLzJXi5u4{smTVR=W|7-!!$A1`FzHyLFS3?)O`$U~ISQ{U>?A+U|g=k9QBaLf&3rLU4dWIkS@8t2ymIY9}nJ zR#WyP)k7|}hi1Zj;34oz4xK&F4p_W6Ls&Z!(l(Q@cF1P+O2T{yC-D3t!ZAaw$|;?g zE`k#97!MA~>@i;v=3_pbFR90XSb=s-NUea+J!Po`_?S_wN|28saRaiO^2sY~VdpQJ z>mS0=Fo@H=3c`8EvGYG#<&}hSx!sS6n5|=lB+It22er1`cHkXi>KapfYrpAFd^#1P z13gONU4-MdE| zwDUlrf=dw{jY?9Fn9~CbTgm=n4mgf zSs}X{xk7fpG9h+XYJ^pHCg-#NmCFa-jiAR~)*sl;`W_`Pfw)hm6PA8`pIT&#_?B?i zE-T@fhU*E3oSR1&KB7-J+k2BseoCV=F6DE&-Y3lZGwYbCs{^g=6aq27ms1j$;j{?C z+=85*jR~{EHtVW#h>tB0va=oKvxPXHEy(3H3#@nJgKQ3TNYr4Eh}J$Emy$X3>sn8VXF~V9kv=_)nTg< zRvosAKB%AfLAXM8*fOCw)nTidSasNPK09ow{IqmkM!&j)EfX5~Ni!NGS$5w0>evQp zu;uE(Ei1jCi_YIyFb;OaR`Ywiw;M{5!<7;sBNMT1i=GD6qxtm+qv;F6iBxmn-)`=V zAbG+Ea%%56)q0Sn|M>akKJcPJmK~A%9p& zQ#?s+0AoEWw#82*EKO-*C4uaz-%>(gky?af)*L6CT`p*Z<@UQOsYk zmplE~MH6qnA-=r5j&(uu2lx`cs8M0EkPUH4gYs!AjYM9sVV zVbIfImgd%B$8mFcesJ`i>UU_tC59-XY9epVIkTgmHJ- zlDhq!(P6(2u-qz;74)F!FiX)h+Zv3?7+~4mHu&Tk<1qV=hfEPVE4)?FtK6VvvnR1bCYLmwaN=7&N&)2;ZV!7)y6 zT2-GPj6xFFMAawF2fz98l$G$~D_5dD-l`<{`IkuYn;rP1uVAD2&F*CrkEfVmU%k=% zv23ja^}@f5nk{L7#}A!HR6=S8(6L7sY_C)2$!Ivhcz&)Q-!tiSfAXuqS&GHqVsJKENbWDf`rAC;4NT4=|&wz9Jm@QU#2w5KW zZmC^$CRmQ1Ql>?z*_(dGX?DJzGHrO;wNbJ2TkgvyPp!M*=U4oed;a>bgrN-2a{he^ zuLAr77Fs3X?k({zT(o86Zcmzoy6EbBxVa1&T_Kz>NKoL;4qvBgla1wz0q%JNB zGW=>0<_fajfnUQxg-jrO+GFL(S=}y6S)Z*uIjaqb%{)e!8i@Tbwcn#8sM}Go`*$c< z-K^n!cFwT^_RVnxnr}`ez`i*q$i6vl0Q%-={&Nd?JBls9&O5dcD_|3H1#Ds_z+J!u zxr?{~+=bwMv?}{&*vElr3izjPNAWmdhZBzjc1W=jc35#G>~Uri?D1lf>=9EM0rqE? z8Ml(Rqckh1yHRWWec$a9tT_jo0ti37cfEYB5nY5AsjZw zK3DhKsE=_`0hgIH4i*(yBvua=G#hvkals%;?+|~_uSfEX(OdfA`Gh{`{{HPz$nooK z5-pLPZxa4xMe&fXn-eS#?-Z54w!(H5I-oHD%J7KKv2G>IBR=EyW#!haZt$k0za-2f z-u9vLQ#u~;S$tb<2A zTY_f=lTc?3Z1$?A)TWhcYMz$V$`W43%HT%OAlTf)t^z9&Axr0*gwd3v&L`xd3*U(( z-$R%rZ>cd!+bFAYq=8xT8qO?vjc1a46Jb;Ho&b|me+CmF-h2hiNWd0B)_dV zg|HJzo;^&GXBU&?=|?1S{pb0~EP4JiOP=3MQk(xw5=dp^^n`(S3@tf2a%U-=EwIZ> z80%1PVmVj_FweFDtd#F!Tq)02CdKoaP0RD0TgB@EwHmsP2u%uJX2PyBimQ9gg{;7< z9!?M^UfKjwdGQ2eT08-n7Ee&7#ZQ4ui)T2~;uQdC1zMI>oFeUnC4zb3jQq{-+p+ip z*1zv*v3)iApfnQX4*MVPD zdDp?SFOg(Y*}qS8kp8moX{ykC_ub{#sQm9R-7ZX2-(jqDJkE*c1(8Ydg2|+LL8V4H@7%!`AZ%e+DR14m!J8Cfr=2_c}8+P3CuXecKG|+{pHm9#wMc4=cWt~1AK>P zk$C#KX{a}Um`44S%@?LJvdI0_oM}JnYp$XbIR(6Dl%c=>4n^D`xI^%OpodTZf~Q`T z$u*VJz4Q=`a!p}%%0OBGgg^*oA(Vqq9zq2O6(LlDfPb^3I>c2WRDjW( z%^%ondU}VsiNNQk%iiWu@F|cwxD5(_naB=*V`C5e9r$o_peSWBPZf6`nM&$wM+Db~ zzhbz!g1NJ;o}Wo7%XG{P~7%} zKMXHjoI|J0(k`u`Q!6nqy3*>D{kS(xm;4yy?UlXE{XE3tIi@gsNI~5t|#@Kd9#2TStGwKB^Y=>9Shc_TDrl zGCx00Zi4(cZA=qoO7u&hJ#oPH8a2%)d{P^jujiWp_ZpaI>%`6`<}$uASP}-5)L06G z|8*kSVlL7>RPvULQh#Z%G!ViVsjoB)GGQ1Ll{$ecxE4hknS+{p!KE2OloSCPBB%sD z;}nL_Nowbm-iV5sAl;cyN8)1^Ga8xwP2P~Hv@1$nZDg(~M~VWCA*yA!G&bkSvaf*E zqTy@)eoQlS3y&|Ma2NUpR=$O!nHj!;6^;{dp_tg*Jfs5WXAXgHWWg7(prGU5_e`t$SU)_*350^b|U7^ddVldB$_r~sdl^W?D;CMY1uGEasNrxUOT%{5=w zxt$yTo47mQTuH7j?#(v`8jkP1DUbKO1@y|$b<@7r@LC*PV6JFrIpCSBCg|~cTdLR* z8^Uuv%C)-?E;xU$q-j;gN^Cz8KVtGB##Cp(eu zI)g>%OsEIzeE2b48}PbgCr`z)NWeQ@1<2mxf|rtm%_=v+qyTL1_`2Q-04vD5ynP)x zK3Dw+5SRW-#K=^r5|t&#h{fr^3u~FMqKHzWb<0QcyQhmPAb7H{0a+Y!$q4%al7Bt1CJ7 zz)ZPQkP3?4+2H_L#+?qa{U7dfD7aB*52>E@6UPqnymj`yWQ3)JRIOxc!9DUfq zrpbjPq)hGz<(nOI)bhCBF}X97=@N3>0sPt?cZhjzJmDZy@tsqod1MkOD|W_FbuJh0&_-XJ3#Cg*$%N5&#pQYy#Mtzhw3fe zZ)lZ@xD_xGG(H`q-?c2m_dEW;LiRgd&HMZcdBY9R^<G(ZCjIcnSsU)?pFaP*vUYwdz7rqH;Ceo^~oIdbOHvM5Z$L?QPe<&D5}Q z%myi;)kV5@T&ccMntzk7^-1S9O1INQ=vr7*`?znG9!8v}`$g9gTcmSdj9#ocsFhvU{xIt_7z-SghNdfg@bBopKVG>`vme*eAB{iB^7=x> zPHTEhADz+k|Fz;5%?{0GoRz#9UZS>qRp1;vf?c8Oxed>0ZN&7y(|@IOly5!gI$fil zLHgh5`ef`c(vT17S~QtqZs*qR0zFRXyR|zjJ*_GFE`T0w6m?F@_*J=n`ujO)?jd@T zFm&v3UOILDPg3-)?Ywm3h4S=(*K(;Rn65{ef(?|LJ(g|LR=L@u&Pe!69G`%Gtn05^ z%5|BBzevrx(?fvXyYw09YpZe{KHxZC8|s|Tk~|{m;lMCt%u(soygcI2Bk-8iD^XU~ zvZ;rTNp)r_K!5SGotT7yg(Jg#4YDmtB*=!ulE%Pjnc=mSzKRd_^+(S5PJ5S zrr&V;;WbCit#;mU0FNs-G%@{)mN&V)zT~Z2tk)2~{12@{1O8EZjfy_@j+UeESs;gt z=(88+NPGJ!7Ks^gU)nN6C68b$1G|rh%s`JI+(-{PdX#d$j!@DKsJ2?cLP~dFeS`a` z0GZftDnO4Me=edtGa-4-Fkq{=2sPL5BjqUz?GW5e9G?Ya2MtssLb*oE@im&1Wzf`W zSp#+fD>Y!}V0GAKI_7}=sByp!W^w9y=72+j_2XFKdT|i(HI6Qp!BNK?a5S<~1CCRy z4(=Te4mlMLJ2=dJ@1Q2GPujDe_2@tDu}{-~FnXU>@38ni+Vz@Fdo-C%w{~$^!~9k| zHKm4TGcvX6^v~CC)A}Rh*R5KHp-0&*RAjNiJHTV|CRU|yT708c#V610TGcgzH)z)* z3vAG2CZw<9vWA|XKRIaVo3GJo({CBQTB8x4wo=P5l)SP+E23}s@&~ETO&S)4t0Bv! z0cX@9h9EgtsDlswxKi%DtqO2@7V=MB;_dl;ovWs|Ki7Txb6xgqzx}!H+n?)_`|s_~ zb@}(ayyN!v=eloyt_$u4ZG)A6gvvXx{3BH!FZ?4`-obtQb6wsQe*1G>-spV$bKSQ; z*HwNR%@1JwOW3zR*X3Wz@*#k@w?Ef~yU%~|bKM)Q^E<^IKi5sRn=5-t=zlEhpQ^kP zqG$5Z=W`T*KFc}P<_LCXPB^b-pqF!BKeY(@KaZrS06n8x4*E>BGU!bWeDRf{8U3sc zPR>>UdSIh7W(#)B;x&1L(Pyhkp*L46kA7ZFGkSovI?*?bY}je6WuP}%D}sJzO&NNi zwQ9P6Z@NAZV5c=+vol*0LNB*g9R1&#V)Tq_Rie*avkZFEwH)-bYh};_uW3WyyjBl- z?U4Xk1O53LKu^CWhI;^-0^A+YRO7yZRw?c%XmoI|LCe8i2(1k6PiWe3=R&Io_cR(X z9o+5E0PcfmVz@)1DZninO*L-baHYz=j>ZSKd^pR1n?YJ3+$Pf0;Rcdc5pFGM7Qsy? z4d8Z^CWaeTngZOy(p2N-mR2cldubyF_QSLc+$htE;1-&u3^&)bYH-_4BY+!n2zaMX zlfo@Ltvqh-X_|4{Pph-VfCIAsBxO?RXZ)~V{-vF;v^NL1ZH+r4GGfi;M2T4Q&pJPw z2)z!zzm~aoWcAvB*N2X+7PtwjFM7}A+UJio%3#86a2GHwV3U%Ao8lV4UGbZ@H!C@~ zB|J1~ivn<4djI3CS}o4k?P}~$gkYCi)&Le?nZ8rW!R_ILO?DHYt?Het#hQsg7`LaB z4%0PlRbTjKpHc!hkyAG)fOeNZFDph4f)c2A#da}rBy4`*Mzx%LSSf)U)onT-RqDVT zQCY~a*=fKMf89E+WFQmcG||Bd8CZwz{wXB`nf&Y{)|k8lh)Jo7XB53Cb>@i*u=vsU z&MG;m_)qz~0s9miNh&As*yRe!@;i4?;BpGni zyh86AiV$v^C)E?jH-HfCnIGCk*R*A>JAO~ez#VfnLysHgY5@c8m$N3rjU#``RoWkjG3=x=xWq-X}xn37(*V21`xL%XD{PGj)F|-`ER;$1;u+bVv)jscM$V^I~d2zMOY&Wl3 zO@A+&ZakQwH4uwvO-E&#QLq}#T&=%Ys~J*{K((+QrBurk7w!@RD(222F5HXA1^;xm z#)Ugxk44lGp+_n_Qm_slL0GpsvJ805LyoMX4Mr@@!<5wW_{FyKz&0Qg`~&6GxtKG( zfROvaKtm#7vD*}isH@0Y)y0lf{F|$5Cf47tniFvEr7pTC)-px~sG!4XyP^$MhevI2 zwEpsk>j=038?kLPoJK+>EskY4kH}-&Q+EfLHRzo5UTO6fRixK>soOgBu7n$2i{9HwyI^kLM5w8_H-@P+0CCssuRm3Q z+iA@XsB0kZvsHPZ-qLUbu3Ay$mWCU1>a_tk>{!Tv8<$#f+~d;}*l9xEQ(O}T?2qhk^3l8apZx2XVI zQU10Huzgw%ww%k*?xw0tkH+Fk(1>4}lnT+fe2rFT8SD-wg8jrwX?GK=qYY_|1opN@ z0=G9=p0+oc1a5C~1-QM*7320MU*q;B%i#7VlfdmwR!ZBOSRHO}x@jbEe^VoY`jsbg(!A_b!eWQMh3mO+O~bK4R;C6-I~yE!#FTOtb$}LIwSxe@!FlxF zDgBzNELf|rE|UH%u~5-*%59N!@X34v`kvz#N=286#jT*CLoF6cyBmlOEntbm>oY55 zr34C1X(Bm_XFYgH=MGTs>JxMxK_9nviIgWRXBpmu>*va~Vamy+QeXw;>_b$A@1*5l zD~BOJnVBvX8?0QLKiR<7h9NaKN|Jhp09D1~$dXLHHsJL$(>>yVGS1iPSq$Mf z1_HN|E8~VfJH))4DE}YmTr7oZY9)>+dH%mhf+$5!V+NRG)kJoZdf`eu;uU zJI_DT(4_4Z2? zU<2(-&~Lv)@&CgwQDk2-f8#bgh}?IEeXaIN>a`xPJN>Eru)6vChxyuo*C2!6p$L>y zv&|1Zrod0dCyA%o=Jp&illgo5;XHG=PQ(^9`*|lr z!H*zJhA>lj-!Ye$lSJqpb7QZCkg^Cunn=21uI`0jhPVXqoLFAhLSVkHQY0FR?6ymvv^_%J-Xi96e67dp zgWXk^GsvjdPV)ZtVb#3jT)s@P&`|8cNoo7v$}|~!Y8zkc@%p>+I8Dmy)Ywgt^S4(G z_CD>K+Q>Kt{zU7C4ZKHalOpE|d`LmuYv6stn?5?x8>S76wLCFvr&k5<8Gt^(??k{q zANqoW{G+hMcvlfq=Xy87Z;u?D>)qHM6XWeCr#JHMCXyF;mk@(3-a;Ik?`;r2#CR96 zm!9h#Zh#*FjWJz@k^FgrjM_x<=n4(Rwk6(?riC!BuQj4C+6Y-6)KD~lRPWtT?es*F zm1-#;kH}mKiav%4~5q0YV~#BnXor*dXAfNgq?jmj0-W zE%lMl7qkCu>ti{M-|fB_1}6wYoGqeJAT6x-`jHG zM952mFbRSU0_h&$lal_#Cw{{-zq7LYV@MSjpZk^e%!4Z#=%)|zO-X;|8}E+7>)i`p zlmjP1GNJ^C6Cor)m;}KJ!3H6Hh+j&2u3wycG63POD|FUW4rUBNha8GzkgR~V|B0GAUXBtS@nFbM*d!#|W{gOJ{@U|PC+ zp}4}y06u~+8Nw6@WU0ar3unotp8j8%P%3>u5wK(tuw=0u*)IuFCqb}6 zNQRI;s3_R6sA5OEdv0-P*)vGTlFuQ|#cK#JA>=`Tuc?3`i-94F#cg<50!Byzq@Z*n z#7Ph)L9jxwK}hdeJT3iY@wj!Rpb5EXHCP8e4V1tkzS28VKS1NA9!_vUu8KgXikPG1@1eDH$Sn>tQex;xnO2?%> z@{e;b0SWg0kNgv%2DqFEApt@ngd_-72w2tzaeDvKVAs;|8y=SFDeKQd$~j-r@9#3f zdhEVC1x4!LWm*M3h4g0-oe|&>I{`4)LFMS zAl#G=g6BR~X6~#7#+tLUO$9~F67O*DAq5;IqSO*U1$cT6g|AT)L4uh6vb;h>EPGi#Twfc~>J${ezAXQV zdr!=d&nrKsbOKya#SkY!m;}KJ!3H7yoq(Py^|;h$6$-mgg+x*OS%ozB)p)h_S%oLm zfeD4g=I0ffrN0xH21J{sj;b)m`z}cIt`6MbUmexjqbhiM4gn1G6>IDDg(PW3Q}Jw6 zg_fdmL+?<1RY)ijD<+Pv(8@d*MEm@|y6!zLsxpt`IHQ0Jj@M9WS6o2?QRg8m%}mw{ z3_>7xVchkVmgbsvaW^zmE~B>Qnz|%7kJ_5ly0f)zYjkmT%WGcA6!TK#0t$j6pjoLa zIE#ILGq$eGIevA2v8nttk%%58mY5baU{G z=iSc&r+3Q`8N*mCySv5IB^j-_4SC0tV|E1NUc~h6uAwr9QIWNCnC2o6`LMz9t0Bb> z*O2M{OPE=TWqqCgt;22@98-rfxDB1Y{ib=8!E+hMG@D07J>HK_fa!PwPhtjU;wj9+ zZ!jBAV-Dux89b{?n$3fTEg-&#g?I^zuoyYWMILOBV|F|~tayvr*S;gf+v)9Rc1rIu?2sD1AoQa z{rJxc-y!@BTk&_ii+^An-a`rA#|QWjA7MM3_$U5_kMRk1U?+BAH}+sJKE*!l#{nF~ zAsp_qM5 z?)l-s@8u@bWWDZynrlC-uItD~Wz?;Q)d+pHK^YCBb?zaRY*^C44wl}vgPpR2PNM_$ zjn7o3;bqybPn)OJkFTV2HXl*5I{QvZ&4{0D(Qh78eRRuN6{54MRgiw<9I3oa1^Eph zm6MVdKPi5#e~jt(?e}KdMdin5oc*}^-0(^V2fCvDriR__GqHzy1?10uic|U=VL4WI z(9o+aYkNxfOGCy3)a4&kymVBl+NaN*GTvjf#>U4ar)N#^(o^eHsORUK@uV84x74Zr z+IB)M(@7`zw9_w8vz?~*tnOg1Ywp_XVcDypRA2v6B^y_^5-QP!w+HceKD3v?cN|#h}xlbKd!Jhv1ex(Y~y-S&uPN&_o1r;jT zFC!_#Vu?-7@@#NSO6i$d(Ma%=OqGrUvK-YB;=&=U6(ghT5+sZIjer(iKUSXG!Msq&FlzBWaqXt&#>xYLIlG zjucT#T3$!XPuJ4&L`iQ;@{@E?QfVzItd6u&(o>QWB+;FNwW*d?YU*g^=y_7`1=1Qx z^zIOyENQc(o|390eNjUSx=30pX{w}jNv}%^kaR{;brtE}deU2xtdeF((j>n--Pxk| z)~kp8;*zWhNvYN}nd__jUgEM^;}R04M8{9^l=_r&tnMB^ql`|MUQT1~ZojOaE?Fo` z>H9&tR|5;4lawOq!3I8OK?6&#U7~hOYTr~yUu|Nu?v3mxB_`vs==A7BUDCv^CrK$b zE!mnBJ0Z?fS|z2J@l&$m;xjWmr6ZJd_+?r&Hql~z6MKnoqSn+%S|Mqu)DB6SBgywN z>76FFh`FqG6x#f=O_O*4*2=Fj{_Iy6!`2g$WQKb)&?L8{47&fjH8qRc4q?ai^(*Qt z!-k(;f8$;2m&xGcbDVmB{K87mG2f~&dfeCa(2rdz%lW-`20U2d*lGt?k*;Ip1kJwdEbBZlgZ3EGjq;&&YYRKcV;eYzqnVn zui7Ce)t0Su>NL0IyGxSYLy}%W7^|10XAmYDBd8;tZ(CAZuBJj1Z@BLfs;goRZCXDxs7bv#O`BSkIw4I1gX<{u znpx`vzSktAaaeG(X7$5@6PWK4V`#G7bAO$oAafXm{t((gXsM==hcxngLl}>sq2%vY#3>(F8^Or zpWMJQ_5YHp4ZRCZ(^;s6(0SIG>Cn>qJ_cF$Ue_tleLsEfD?`vhaD(6u!2^OGg2D0J z*K>H=QWwAl^1!aYLYxgD2f|ATFIZx4Pe1tqTMm)uec)gzcBr>!S-;CFRStZqJcK-; z(XU=XiN;-J=9}YHl9h?}+*G1ofVEygkTobQzH`gA%_4%V4Xl9;f`i$OXwT2OzNfV5 z)GWSJJ0+@l$Cgbywd~$3BCvi~y-;gFaQ)zLYhZz_uKz<`w}N@>hd!Py+ZPnoqnv2gF7Gb|UK8r*`jKbuJ$I`$Fpw?(+N(TE-QZ<#jO^&;DB3ExsU^N+vnK63 zHjQXJqR*(0<3ETBXwx+!G%PeMI5-@s7C?p84+{#bA559uB7#FhgX`A|q|9y+p@Bi6 z;UPhR?Ebf2B^)ybc-@MW107>ldEGEn2)2fY*9!uxSm|GUoA&KnE1d(nMFfS11lJD@ z31bQAUZrdyLG{Dx$2aTNtYiDmO6QjC+XZxusACNX46}yS4=cd$76I(w`XPa#1=!uO zcxb)w`l0pXySH!Ow(a|EIzvfR8f1o@Oc?cKc7>lS_Qd#^lM4rk3adbRZkf?1>BV$#_mkgjLH4jpz|>trr%? zdTjElV+oB2(sIIBr%hg=ZebDi95Xk0jnOgxC&rifzlkYZy!x4OP>I3i7`er(i|i2# z4)2p(!M%iqhMbmrf2-GMH&G$-d#LCbxznq?PByVUyS+BZzq2u!UL!1&1C@A*`FvB3%V$`@pEMq%&Rt?4HqBh)uleWpbn*_lkC7-g@I|w)%-zxs>^sf|tS8Brv{hNJPir z__m=D9fRWAhDCG)PIyH7sLs7Rc8zaaFQQ4k__p;UnuNx;4YWoy34tguqDeSJK@m;D zAPSCX0<{A}BAP(;z|e^HZJITOL|8=EP9P~TJfc&V-kmxt9XkV24~e$zn>K@V{fO|; z5M5}X!}^m~mbatNJ+C=#jwW}#4tOx{hh8&S@*}TSEP0<-RrxW;|L%UTc8+9|*n> z3PUIYp(tysGggYgnb99eKM0i|RDe(sLS+b15ZXeh3ZWW=>JVx`s0pDKgof-p9W*YO z9R^8q6?O-yCd|vtSgjYzZ3kuALx_XW0YXOz@en#gcpt(C5V}H$hjqRi#N8qEfbcPd zUJyQn&>KQDggy{Hg3uR2C-%7;Xd1%4bu%_9iJB$>Hvxj5UXs>BoCtySX=ZHhc+bbU z)gXtljKaprUcpvtNC1v7$E=dZbvj+Fj`=?H>cyfg#$s$;8Dn96SU_-iczwtIGR7F4 zM@UcrbbN#I#+n90{g8l=di8=FL#i0(xwAcWjq4rjY8w5_o*}`30fE+_VAdeO_`aiN zxN)-1Gc>4vKxj~SDDfEZy0cPMjYS>f8W=xu*IjgKE#!_1iDOtJ;~Zm3E8|mn1$)-Y z*xn-^T*Z&vq<|kdMz%IC(XpcaJwIb}+8JlcR(v|iPKh#JE$A?g`%%VN`8D3ij(_Fd z@8rs}x68m$|Gv@9&7(G$a5kIJ6?~D{;EO!_eph3IM)Dq(G0Hfar7kd@GlbR;u!e>P zGW$Yf14D3qFrhV&?OAB7VhFDn5FTO;a@<{LEbPPbzBRUT`vQ6!6s#7p|Sa8=f`oE^q?KEXAu*;|9AMSebU=n{+9rcSKGuSP4&Ic6+V*a*^mNa2f3 zyQE)}j>xQo$yCzq8q^(`Z!E%ct{C6(Ie)>Y%C8UHo34J`@i$A&LSJ(M!*_xJgu?~A z^Yqwj0xJ9l%2T?}p7l=HrsnfgL)x2CVw+&y_Jw$rxCWR0Rh8v0j4g_(rr|ZW$KU?f zHCfb2qk)ybXw-W)@4r>P{cX6+5+55&x}kxTgMiWJK)sIEvDqybcJOGk`Ms%=5o7DZ zXk{JQ=95*2UaL0p!ar2wc4bR`H5xq++&cU3tD!Yn?nPrE_kRh{%JMH5Yy7oztCU^F zwaF_DW#<~)laCfInLJPLQ)3r6;BE++5O8Y3@B^gxxzGn8eJFXkp$seOX7x$-m$#&3 z8r}caI968U0*7n#oR;CC``3_fZxhuHJS_U}t3_@V)jYx~|Ep^E_ZtJ*H|JCj<=u$A zKQuhqrT7278>w#AL3MaIUEp8WeRcpkreNv6_cPGc29`eIXZkuf{yo6f`vD+=;{cnE zc=GS-j^6k00S22@&CVv0w3CXSbW$8yZ^WO$Edh5$Dciz>F+W+nkjU!j|?VK_s|H)~l zbyf{7so09l|00RzeDA%+LVka@G$IdXe@Um)v7}66D<8w{Bi%mT^}OAuZuj%9PBZ<; zz^&RdsBiQuQtjmbw!~Ul`blH?|6!fwu1w-iU)}Sc#G!xGLBHe=0gal>l#^F{|G$a+ zr-uOam^$dG&PR&>APpCV*B(&*MRWfN8?F^~aB_2E`xkxjPtw4@OR}7&#^SW>|NX>I z&qW9J!a(o@wR~R11*hOt{y)^kLF9{_gZ1PYY+@)Fp138~L z|87EdK6bd*@Ffdxk6kZ~-)5voWPD71J(L;f!R#ktzWLmm@zZbi+{WMDuDv1a^T)gR zd;_zDrP_;KFa9?fG*MVj!$i?w-iYN#E39a|C3SwK@XHl;^NFI%Nn@chuj>}{KV#6I z!)bm{n}iR=Z`LFdC!QnS*_sn?4hrT4KUU|I(NGay3;qKd^sd@CAPQ4=eUug>-)wP1 z3e7S~4+aw_wI8>Lv=0<@x8e;cr>iVzPn+p#3uf39OtvTcHd^!EPLza zR~PR~Pkm=UE0Aww>u(x|{k4$O!TvmCEL6Vk%&l+OHW1XBOkafh5u5g(gsG;-he|FH zJ>#2sXWtMWn!YZ)AUfqC!Z(*EjJxQsCKXwAn8%I3GkHd?v9)Yu9qt(?`lPJB(r4Qz zw_C3?UD!V?F{n9BI_|PrGO%j7f0wUTe)YGR*hpUa^!waYh-{Ac0D`DILh?_;fH!~FYymnU!;*+JP)HBw|3iWO_K3M7XSS}X^7GJ6!p(78X)gv ziDpx4I2OmoT)LMn*SfhzZqvM`4q|q)g2^w%VluNrc2kHOynVCC<)&aZ)?$ic2}R5e zQVN+K=|6*=(O-M9=Y>pzE!2z9!@M^~dYfiD#@98imYLPZWHg=b_DCvb{zZbAW%!sX z5H~nD6PH5-yi5Nu2VQeMD% z=}bMU;EvdS;ATMh9>N9)8zF3huo;2_!X9=>XDU&D7bJH>$b^s%VK0OqAnb#HVh%uD z9`^hXL3|j(j}VSPc+Tp$nM%l~STi@%0Qoli&dn5F8Y{ej%$E>6p%OMAAJUSZ<@%UP zVq5jz*m`X5U#wsfT!_&&wM?@+{~>*qbxJzZ^ablv*i?VNr|= z78L>@b`gNwA=$gNvfiiznO`WUrLV8tqN3g^LCoEoH~=b%`Y}^w)-pjAlBa6n3>R6o zOi;976=b+T;8pCycau=xqR@7?jN8(FU_i`>aX#-JF(4*}5ld|TX_wUJCi5w3s%`9Z z^BEOTY152FPl$$?x2Pq6MrPLw6g!z%6PXZmGx-z)JEMG8t~b_46v#vr*d@diB@rty zE3kIOO?t=o#Z2AZ*cXA|_1`Z`kiY72NQRj0DFy|IyR!2q#D!Sn>b=!+f63uenpNH| zWBCfDz(!9N4yR%ebH>f~MlmqbuJBzMus_clYWhx|l zk33XWb`P}37*{@3lr66V)+XZcSqbB&gR|NFS}OCW5%-8Wy4EX|sgIE>V@ZgeC4r!l z5Em#(im-;Nh~?HcxjQ=6Hs$I_XY1H(0y3oN4c4Kqs&fde%m`%W+6*VN%=deUxqa9P zl?h_0B*e~=Ku}4D3zQ^9Sc5C7%Uabn1+X3EP3yFAic8#OKa(H((9dKht{BcvuItbj zT+hEOpU#~5I}Xq;^O?hkG1laJdWSYSB=ybAmN5S6*l#?iP_W6<`|~H~q>m2YlrYw1 zf!V{r5qyGSkRUg4>(b@*yzdCW9CcmR=6L>e22!^46!5WQ>$Or$7lbbd<3Nf>mH&ypWUeb$Ci3c$iWcG3}qC`o- zrJ~4GtFYzuKoM$v^>74f|LyS@WY#jlD;sIXS2rxhQtN|2)5D>p24WZPPfx}Z7h*0E z(TSph=@fveUsV4yX%g<(uJ0{q+h$A!kG1G1h|k$h`;$)=JfV2iFu=J z9+s?S5)6oW)uMY&O31`mEmh6CRW;7UxBwFfS9!do&IwGN-@rka;Gvw=|V!JKCF!-rJv^k!P)}FMs2C z4r49?l|B5Sx~wWCChOS=utZ^tD#u)a%;dTdLIWl#XTnb*nZszQi!v^4$ z50yDDI!IPCiD^OtnM=+dVTm1JkWf<(p?FH=X{nITf;U)8rHYhUv}yd}H4p1;WAYhtJT3!=VkX$rMCVSH&m&BlPQ6?3xi zN<;G5c`{}tv@}(4!GT$L;-MB{N)&W(M($QQ@!t3HgXDAa-nl1<$+B9QYNvDn_Z|Pf znpgy!Qv9V5EzthRHTgupYx2o$H|4ftf8$MZW~*$ub?&{t%U0Pko=LztCil*Dpo(|r z??HOPxqq;Oypu*s&#K3zXHAaCYUb-k6sL+}Z^+rvsua0Hr7d#Kq`6d)j~uDSk8<|g0XiK@btKtR{UA9MjV^Vl$piXbbPhRn zP|E710X`1Z5;S6A96EZ;L(&2%zh-lO>sGI?d#fgpOcYEn{-^w=Dr#txm>_uydsb+HKIZq!fA>;Yr&jHDtsaBKKOwLqGP)VFc zEA#=Go%?#}I=%C$+%~p|u1)JXoT(D>*z?7lOz+O=@<7udIk;~Q#=PXsrgnLD>4mbI z$(d>ixqNb-46->>C1h10LMq4Fd<3(5nQHswyuqDhj1RkacFDQ1Zl>Jp<9FppVPDIG zZ+CI#utj~rTvXdE{V9!l^|@SO;d@w?mx<{HPeEUfnM2clXo|tMTXEuvkDJNZ9A*-1MnV7k7{SjiCR6@@8+RE~~gNe{Dy>E`(C+}!;OvZSy ze1_Lghv|65`W7TrVjWp9um*Q!yrkhime|cyMb@#AFdE=0 zT&eyP#rC48Jy4~bRj%Y+mtRDEB`aZ%i|gQ^g)xVqX*-nfBwh^O6PB4P$8w`Ku*|wW!I(Oac@Biq)@XqAu9h0DrB)Aw z+0C!F$>QDQivx0va&u@Q#)$*XXb>kfje44@a0gKjQ)H1t*4E*?hB~~}b08Vxv9CU7 zBYT6rkie&TaY%$rcC!~~UNgvKVL1cgxqf-?y7Et>f^?3=#ca+Xs5HLD8X1VcGO?tZ z=`1O#LySy#VI9~?zS4V>90>2bB(HI|R3!e&#FA>Jv!tjFF*1v>NrO!l;ga!ac?%me z)HI^tCbHxHP?NXpIWAst?-T^iEzdI*k%LkunToqTyg7^o&I0o^UTz9xbBCK6hA6P# zn%>hGk8$Q7?^0~v|0)jY$PT@T_TTk8(a}S=(3IIAB47j@qo5r4%Bu)VjWgAMBU#Kd z#?(v5Df%De=tr7{@pi2o`G)OcMMs%>av94ceHM82zR) zN{lmAVxeP9kt7H@snmZkZt=0EXd$Qce~<&9LUimZYdg+VN60JlALPj?FdD4mO(oo) zcRMe!z!aFPRG%#SAH@2^nToPn6HF~g7+X2vO*=uJ1A?kgG&SdfQYXHtAV8#g=F+$~ zl|f)C#H(PyBmDn^iAY$v1gEeHB&_cL;e5h$c2hIaWfF_Co9f&C4@Ic+oaDw&5A85< z+2Lze$#-nOQ33ve0alCg<3R%kjqWqlBNx~e%=C`RU&Yq%%aTVfxFTm(^O9u9gnv2g z-RZE?rh1Y5A54@t9S*Tfli{0!)7h|uiFp?culp}jXP8Pc^AuAF!%3eD(ui+=Vu3Tj z$=;m;%4k}jlK7?$^5+y&ZMP$bcB4dfjhHg|O@%$2YO=bmX&TQ0r>nxC;U52?;SZ;o z>bQ;m&c|sm?7i9nBO$EL9%=D3PIIsU;A#k|f zLXPpj$eCg4E996+PS~4`NqY8RC1#o46XMKo4vU^TYJRRhb+EZJO%4AOtWsY%VHNsM zu)^G?#tuifbRCBCKL3yS$_zJ^a0|csp^LvKees4ahPJctdrnb*9+$`d3d6DhTo{|a zWP{-Uv!15Om(gNSDoHGJbCTW=CJO2yT&2>R_X+%w0(dScJlEO5l}Xr}`HLatb9j{U zpKmg-D#^nAW+%UK8xdR4c}{Fq|Np?oAM@Cp`TqeHH$A&L(qv^>u_p7Io2$H|95dCD zJz`)%ZAsrv7P#l)7`ea%pD7=O%O1aXXA>8i+Ih@_l5Gl<%$f}Mo03PGIB3Lh-Q4zCo%MxHap>1OHJ-E*eWwhxC7D?EGs1Z8-)COek4 zNK|U}i{Jji@(Cz&)v_juH0b1SiB63SCORu=Gtt%4D-fM&-A;76pHG4OReSR7uJspe zxKDJ>h~x=Mt%Rg*JMY+S%GAXAQDvu3P(GY8A}{HJT`6zpGyuq4bk5$hzzsq6MHWmXred>FcKzsGqugoQ)V7qBXI0CH|A%?+8GX`q-7?(>M(PwtUvh zSspDkv3)^-Wg8JK09Bt=hiCyP{Y9Aq`Ky-NOa%m3{a(;hCIR~3x&K!|4{aKt9s2Cs zzZ|!@0*bzKz*!yuMV?&EpHkH>vlg2{v;Y+Os6WvHP?YU-lY#|GR4O3A|An4t0@UTX z|C&PilN6&Ztvgz1yFUBTrt_1Ol?j{%0QSSDCSjjbd5ELG+e~!23MlISe4+&)TXfba zVhBVr>)RJ75dAQqfPl!eg^4BwgAP8~cFQJwhVn!U3vEA8JVBAC9wk7~ zW$wct#4C#@a~ds_c#B3dw@@gUsNZouw9!OU8+6&VYHPgm>Ce+r7Gz9NzVz(TDjabT3Q$Jg-+xHU{^X=01efP*DM8kSX^z)|A*3YXKrvRNk(ATEC%+DuU zZuV@wN@v&W&gYD0%%+%bm(Fpe0clK?-Em5_#bFy-%_JWn`oWxLJqst;luB)F6S};( zKmsx!e!`vR!cC%cCeI@}lX=A}%eVKHHUYh^X=!^=Q@rA_T~C1EQk@NH0SM^qp6%k5 z{H&g$d=Ap+Osc%pU)mGR_1U0h^J9pS+h-&x%*vP*ujr;8eZSBEo3hFyyYsSn@ye3P zoNo5a-}L8}0_BsgWV#3hdHENK778=Bzal!T;zHiQi~hW?5fJE?QeQP8hGv1S9}}$s zI%7}J0DTL9_I{R708p9l3Y1SWY$_dJjX zSm@bkqO+R4PxVu0&fw*zhY+1vdI8TzAAts@Z(BliPV>9GoXDSfj4L7mUwjr}HtwPV z_OB;V9C>nm0Xpi{mqf=*_=4R{H7&LI|6DUc$*fkT#l`CJ7*`JniC4NMclmHI?D>ta zSn=}qG|>V;>Z$ug`>ZNNv2|o&>I;#d=^ae8s2@3|1JOc$^z0!C zxbw*Y+5lnc_yi510tlzfN^n7_AN9wQ0s^FQ+*yPI|2YQ>07!jujc5(vd_9?50~lP5 zcM0yIqW4BFY(@+X;fCHsODj+CalI{;S9mvr=-du=qI17YB06g6RHCEhm;(7Zi#rsk zpP3j=bhIizdSazG@-6|udZjq8pd!p-g^9MREfD#lg(v{KN?1TBAPc=*du+LH+c;&y zEN;R1AEah3ZXc)Y&daZh7XE2H=G)?wO=EZ$fFL1&`Uq&DK=h#iAo^GXsQQ4&7d|5D z3m+2l$;SwwO+|=K=!y`%lq2K-(TiOH;!d|KKy@cv01!Ln8o;*YxwEPkPF=@$(4%d_ zge|kV>D4}x>h6zITuu1Z#<&6i-&q+~0N|J5uX?!fEY#uu+y`g;R`MC3dCPjs2Hc59=l}nISby4MpsaMyPSdOoI#~2Uot)BJvqx9r+hYJ zmD>_nJRuG)SBcxBmIvi0)h<6#dD*Dp8l*e4XxJL*@OP8Ja7DgHcoR{Nj~rg-U>TkO8yp(cOm915qK-5N9H- z+pZ@kk^%7Up9f$*(fY9#L8lK=ovy*@{z_Tn_4Teal#gj09&c03eXifQbdv-iLphXN zZ~wi#0m{eYmyG(REIDE7<% zUp6jECXYrs>D@}HD((MieHyhdnL_3AuKS+%)PVnmApO6qV+Y!PLc649z}#@IFkl=0StlPekwQ|j-FZ}T*2fD+SL z>9gV$4X4QH<|H6bcUG& znDLV6XnzBzeM4#3rmFe&p+h(=08d>CG>ktD@vy^GKmf=bbBE~Y>z|Uq=!TvY+w-<^ zdTU7@cgv+XwN-ges}_qE`2+fpMMZt;5@;aFhi~=Y zsg|wUwg)Fh1(Uz{AMqmDlHpyVBX3qEItSecMEEy~AX@lWWUJO3i|T*&qESR^{;AT^ zK-K@%^8P0OclXw9YkiE)$qBF1D;MTQ%MRaQ74@BoY|7L{J zE3V-O!8M(9Ua2$htZ^Hxd|IZVH03y#zwUS5dwp_8DE&_7jK8tgHb`lgGP}sU;xS6r zEBoj2)UEqHVx5Z|uL3q+FOZ))$)DUr)X%Y{cO-^}ICKKhgm|fO zL1aYyQ03Gj4)D~&0k&_q4OJXVoPaZSI?E&A(5nUH`422kz9SUcFGdk90NbL~X9*2) z$KwSGq#p33$xsN$dD?(z0^GEh`=c9ehbvcKcB$U>BQkkr@~6X;R%68nk-KwN+}JNm9Zk{6>?9LNx0al%w z0Q7wfUJp(m(2oGxlmIgy7A^Za5v#*SL#z%fAuv@Pc0ysKIxGdCX!YSq1Kh?=!&c<` zpKC@m)VEQeJTjeMUT5n4?h~<9qWmv{F1-!O^;pJPx0y* zQoCeXZsCKy`zh64!d-iEwkm%^!S!~{t7so4~S|2_%d`XXqME4uNP)S*~=i{2m7`HXNqB>N6}pbHpU2RI{JY4dqL$&pFr^eb^wcL2(P!C;D-< zAHT#WDA=;zt?rMes!vo&xQAZyTqx4CbP@ymPYxUAx`|G-*hFu>=)t=0MvwxrVH-Vs zI?-as*M9v+-T-wAI=Z?qe}_~zd?N>U;nS|V2Os_00vj>J7IbRqlR&F`;+;&pzI~W8 zTv?>0Ror`qNbihjF3 z6D^i}~)Msvvzt2GIn-Fn#Il z7275$R{wg|=vpI`vBmC(f4O~<(rH;=W%h&N%EKG&W~Z*?<%jR528%9+w`yRl>ooWz z_IdV$B46|{j8Jx`{gI7y_ho&iy)=(dTFyH14C%EI%jY6}qols8>off0qT{JUi-9+A?#S3E;*brH>Yc7T%9I3oIK>Bj}iM`iRRT3EV6zB7Zg_iqzN z&8ZQm+%Gq+R}Cpv*|M#2*)JQ$DW)f-8eb_Eix$RBGaCO{MLdWhcGc3S{yB;Yz_uI4 znO7eW9X)L;-(?f|%6Xzi{iI$`h)(^wbgTkhVQ!Tdr^HPZMP02KY%459^~sBgb3n6DmBiu&UGQUId!&HT9F z1}dQK2W)Tv+Ao1K5RrO7N)-^0dVnef#;6CZ8X$21t6E4L&}#YW39hIwPIxsywz=06 z6jwxeMC=MkJwnz1sYlF0pg4lo6pABi0Z2W=*7DUeZc*PR4#5Q?Iwb$o-mQviB05Cx zHJQ_35pl>a07a^Y@IqjedPpx6Mv7B>6_7aPSMzP!H2@(%ToVw0=o-P438_sH5S)wB zHG|(LavA`MYX}0+s-^szf{-WfF$h4~JqH2=<1bac2q6lHixNVBxJW??&P*PWhZilT zs(@C+Cw0S%7`L2&;HC!MlMsN!O%6?-deejS(fAivK~xiot0DrBxJn`fh^rcT1ecA04y zr+aouRK84}JM5D@)_S{X6#QI{lv;Eh)HbJhnEovt1oKfPX+U+Yni zj&GhT$;+!p$2k){iN>R2n*u-oBYk)2L8Fq_?aJ>Rt6oG9z{>4*jXF@AFW-Fe6Yo$} z$hkWQh|W@#fg0eg)P3JMq&O0-ZA)u9y8 z0)TzD8!xW{jJ7_ZHA-thl6o{R8i+Jb15&S6{2fBA;8SV{(L$g#^i!gRLh0ZVZXp$r z{|Bpic@?nFlXW~_G)T>lrWS)fQ6DTqj{!n~Fqr@#Oez49$<=Pk?PcSS3Tij`eBZ19 z?LVg?(W0At&dB^xLhYv9E!Xhb#U=@*{?)SRVJ1fHCuzZ6q6KQ7r#pGR+A5JRx=E|A zT18bL39AVJ!m1n~*kAOM)&jMkv=*v#&ZHH*1!_0tHu!OaKtwBmk1h;kQyGIGD*Fn@q3v;$X&( z2Q82#Xis9m$qQ@$ySW(6Mr-aY>20h%PFYs}$&(-9R5Yyf4WiGkDM=SNKbjNz?q}d} zjYo_T7dnLp=A7P6&w;|ja%W#8S~!@`+NVSd2g`}zw^W3K*`#A-;%PQoeP>Cd+EYhq zAVNFeAld~)`PE=g7ZAd}0)#N103pmLKnU{*5MVy;(i(`gJ4>>!M2=E6E-(B7Edh2D zZcSx?7G5QU2(J@LgjWg>!fOfSFO%0??gw7JD``euyc!!~>wJS0DxGf-1Lx&ccb4Su zxjEJqgqQ*a2r&Z*5MqWBAjA|XK!_<&fRLNQl!zc$?Ob!o0?Ts_bf{RK^I&<#+jnAl zhIck`8&6xFH}#+n)@J&V@kEQ2AnWr5M2nRo)o&dyFP7(Z+u_Xwn+?wh+VYGbfF;(j zOelo3SmT5cu?7ky+8U{X5Nl{Ef)vX$yqVG!L$N$RZ)$r+jBwBZ|9q+k@{OCmQ)oHL zY7|a8Kv}QRNpaH?l!^}o?2liMn9&$lnPwx#WEC10ryN^TG8$JUSOAC~QMfPB{V!Jf zeUUGjU92KG3yKjfRvD=}zpNxyp{%WNPO;^oWH@;1CNL;T&1GZw@YWEn1wuipInV_~@FF(q341 zU@fwdQULL)2`m&YAjCoSnz~Wu^fQ0&`~19j_#=I_@{kGfr7{sA(e3 zz-4^f5e-G6#S$Q{Nvbn=C2@^0r|uZ1(rb4!NMLZEjLdWCj+#%*pU94geI`#g*GH3& ztB)S(`e+huFFexq(S&HwEi5T^Z-u3;>h7)R39)-CdP?lxik`&H*}v=@^h6M#MEH={ z!4*Cxga{uLN`#LJ5W5x41Xd8HA|XHvQwk-*)B=Ro z6)H&4Rn&;rwgaS`i=;!mP1^)R%ppFJ=Xe2iKOi^#N20|X#d;&UNvuags93LrQn8+iZW8OA0BsZd0iso+fM~TQKKv+!x z5M~tsMK@^;P`gQN2dLcy{-^E-hzeq*7XrmfFBFQEUUZXqUlo99KLD(%HAvkK5G~f~ zQ#S>G`PB^vVKMm?5jD@*%+dQ14Vj&OSEtxTTakVyn?#p z<=^4nFq&PK-dR!?AFNzJ5bXlOA0NbAKnU{*5Sn>Z5W+eF1Xzc^-U|@IJQ@hF#65h| zZomJoFIoaxD#k9A0a}c0Awx_qLWwrHs364TK_HLWnk41p6V!wX#d@P@Lpt9eg@aZd zCmOp0EO7!K#auuL&lDhp=L!(Qvjqsv^HmUHWaQYezm7FY%Eo6&0RsA)6uN>~ed)nC z-23hJ_|?AgPzP2ZqTR<@=q`bWXTwC?pzk$3MBFXV=I)9k_?0eogFbpuI?*`y(`IAp zshh;m7KbNA$kbx-uone|Xo)qK9yG^Y@tc*V>_8B}5~~!I0a{zdR2gEG6FS5ysDgl- z-0BW}B%)kBIkO$^(5H_*LN_L$m89_H^an&Eh~OO~g!iZZ^z~2$-T&ykF7M0v-Qn{Rp=fKx4yOzgyXcBd!PBCCEXg@D4H_ zhbRn5qTx)3=v&#|FPcLos3S%c2?bh=IH6QLjtNlWaV%N|Erv8T%J~Z7L3fB)^v|YZ zeqOgr=i*BUYTH$Yv}$*XfM&10HJ7sCEhV^>By2^ultcv(kfeSYOaid2@GtUt7HwNg z3!tekcf*N>d~tVNTy)m%ma7+`#ocqfC~ZY_sV!ppl{ax!JNj#)#Z_%msNNlSD68R( zgB^ZmS{53)trtC{<#avF@8rEsj~c^guT``79YCZV@H5sXIoaxNW{E{CgXoVZ7(#-r z0r0E$>~d{$Y9q06p4;pJojr-aobWoGHDoXy<%se`n}<6BD_VlwSVeF1ul_BY!|zc_ zItXqM+#z^C&_gglIOxI3>&z8(zIxVJXAY{5zZy~z`2G+oK==~E2ndxSRDn65X?V&3UMer4bIj2>8Cr=h&# zkK*Q2dOiKHd3`i&Eq1S!B{l-O`%(rSEJi+mOlL5HwwKvL`%XE3en`q*r=h%KS$XqE z9&C4Y^K8fLJkw1bTkd1Frc|=TInq5W0WyoKVJ^YeR=0f3%r(tM$MUM?=;HKC>mlL% z4`VcFVu@$p!N6P&9272e@r*tY4jkLLuBc`e4L!BOfq`iI! zZ&&_%meJ6zoR8x8jqcrJ$t^R#I8Sbz`5JAH@5$X#Ef2IKKrx>x#PZskufO&uK<-DG zlXYxkCv)L$R>>k6rH`dS(f|mfr9RS7fG0zMALEhQOHop1sW*hKK*_aO>$DDRu|Ww{Xk@_Zv^x4_?(p&uIRYHr~%2P(Z!e^lkQKbg|i41a#57AUF1 z*u-w;!IedR&bRO7h4U3uYCS!TS6Ss;w%OI5qed?zxleCEE#D@yal*lkSi@kr(%PC;q6@ybW{+hR@bga$InX@TYcGh{AkuPH_ zmj_z>^uhVErt`}?FP+5C!(UPFT|TVyHu zpIh>?)6Cmp&US0$zlM8ci|9lq_`hXVCVlxc@8R^5Ou++@SVb!IDvv+mdoY@YQ#TqPri*gr6*q@f^=ShzuAl91cfba&-(0# zbg#~jRl~Mzw_K}QAfW#=MT9n{G_DR`=0&WM{+v&-cn#8vnqnq@`ub5jR!h$k&&T{am zJopfI20i9Djx*mMRJ<`~RmVVCg(qC(RZz*`-oJ7J)pr_{!?j>e!&sIv4C-T*dEKt^ z3~Wi*3+@+@&v(dc-{F0Ev&eSY&x86;kADRGOiP|FE;%&*u2P${@@2$HwLj#Ldv}T|z(LjCalN+^`?|8ORGl>B+gt7EkMOYDKY34;O!tSVE z>k!NkJ%u&2E<{nHZ&8QV>8M?J17-*}F<=ehKzfuVJPUOQmvaN{%>S^tA~pQh`Q5yi zu=w0Z>I8>^ay#zeIjADMl{zV+?hQ@0@jk;d@NXFi6khO;OSqnoOdi|iDPxj;vzI$1RqfiLl z1T#j?hC<*zSOG_?9{oy4LWdJ74CslPa`sE6T&g06rN6l-OCEfMo!kndsy90&UkIW! zi#=xE>DID|3Vzz&lXCGOl{hsrOJ0_i!CR6Zbwd8(P&yk4cF}Lmlurk);2FC*X3B?u zTh98VKnA4uLz<=(vt^rHVyU{sHSfRGxvbU;aX6RNan(0Dm&Hx}H_(C^7z4syd@qk% zvw#b$J;sv=)Z*cvE#1r~_y&)Y*;0v%>LfI!!)2*^9A9_zVQyEXN{9F)l=#CHY3JSp zTvRXVs?_q5O8m0Y{@R}&*=dtm+R((Uio0?q1?hkKC)mQ((+Z%sdI`)yO-LdE{ zWl-aPNY2%&=*KpDpvh`9_n~xV0-q1{Bl|v*euF<-k1OEO)sLiKllZKM{PXM7Sr2R6 z_=eAVC~09gb=E^2AEogX5S7mxM>8PvIcav+-P=Wk9^tAPdTfVU!5~5Vgd_ttJpaQi zZXYyBY*Up$V`&7MQ7eY_6$12Vc>!fy*KAEA{z|$)Vk>(=1(S^a*{QqSuAhj==mQyvKRIzc$vmx9*EjJ#CFJVEi)U&2@E4f`jz;H?X#C*I zhedgV{mVnrjPz7OV$zSmf2|Af7U5j49PU2%g zvs5Y2M;&1`il^dFOYu@jz)wnPrNZaBQ-&U^Pf?fxhBKhtkNkD4!v+{encqm|y;+|b zz#nyamDD8g1-yQhH&h8)Egk*(Ii>N(n;MMe=>zLFW11~L4?7%pyL}MJfyrv7dqb zt>+!mdws4@dBo?MhF_5*KK^={Yx*18IpS9?wND!JJ5OKxcE7ZG_b(*By4OJ|H=FI5 z23>KeqCI8baHjm8Om)d$Nh2ncKOgEan~1t=&1PUM^2J4*x~#uxG#jB05sv zvHN+ggIC(mOQPd39eIH*?+G1)Wjf?NBXC%EriFVR${h2A(t6ZY`&;gvey z<(Yj3JUO3jM1)~+hm(>>cQ2PEl^mT%LPxbdA(^J+V_M&Ez%fY~#4=_ND3uQ-+5x_re&{h4b{V%D8&&SL8;+#mrog>Ja1N!DNekNzriKOAl#+ z4CX-xNyQv^bJj;4bkW^wKM}Nx)NH>KhrxHBR$V{FwpYm2pIVct>BB`);HaFrL*pmj z-X_W$?seZvf=G4MElv%y3T<(!F06UJ@v(&-k8=W{pBQ^-R`niKLG=1A+*K0Vg zCVZ#ymmOIvP1(bps!`r*>F{InBLk+Fe7ux<-kG!$Qjmq+&w|-&Zx@~1tmka*J6FfM z$%7|-!HJZ^?()M2pK}6#+OE$JDq+k0mWVyw!M4j+%N0t^V)utY`uq}kbjL}oLw`uW zTr77AgkO>F4g=uJoH(vIL0JqxG|4(ZMaQ=8=Bqm0s|=Rf%UsRv`t{#gBCK=zm)GS1 z5BaQ|_}xwU`++xc;&X#E^ZC%++zmE>Fj0|qg1pmtN!$n$Y1{`ImOu+JZDqu^<_k3L zafEcaOH(Rtel(e$OKIeH-2Q-1udv-xk{dM^o zOYLd)HI;xW=@9Mj&Z^&B~dU4{9HMg7uLJKUU=7jF}Lmo6+t%Jm+ zmg>Zb#flwKml zy`pJFLc@doq9H{A4^J3*gRo~dUNCb4-QWicpPWhOsYZP0V3p+7Ybm1QC1_tnYMA(I%M4GTm#JPca*_@AIgk+aGP6#@I$Mvg%X9E`v`v6Ye6V1U4 z!=2=Ls88KurZlcsxc97;!d+>tKJHgz8^uPprV@9!wZ`M#w^jvr#Wi96gMQ^IQ4Q{- zYdUd{T`PvW@0tMIm)At&jy)+=xAL_N+~e1(;O@UB49^AT3bW#gfkxn2f>sPq88iWS z{(z#z!GzXMJh8wGag?Fez%vd_7M^}!%fzvW)=E4t(MsV-idG-bShU9A>5HZk&txPSX8G9BNFo4MlCV{!Lb(Bit1_{LRSXA&P| zitA3|gH3TCN_^rf?o06xKH+xO+fR|j-8f+~ad}RdR9vnjlfQQLSagfHxhrUKp-Xg= zxbP)psu#lEeu@m2Zr*;1tX@ri`zi9sX?Wf4nfB6(S<4^Mfw^Mic zt=EIC4oI%)xoh}^)#m+!Y>fcL%0CpB7{p2e$}(FvSo1j=a9y4k6%Fi64Ma!Aml@k434`N zOY&=98!t7S59y#AbXfhI4GBk)$NU~ei^bV;0ir-Ll_=-P_O-M${N>YQ!^U^QbSr!F zYaA@Pw8h+e_YB@?3MX~j-3oOS7Pmo znf>W&a?jA9`T>E#!S(nT$GMd)v4+^Mx65T}OyaGG`1W8yV#1nfJO_&vAk;eLSedF8 zv!iW!%QT(q$Lh;SaPP_^oPrAwJJZHG1s5RHV&xolD_So2zy67|U3VpuU2bBjq{WrQ z&Fy^-$u-T(m3ItpYKi{KSJC6$@;34NT4R6OCAp@rUf~zEOI$cDxu$uUaxB+k@o-pM zShBp~qiEw;IhBp+Y8jyo3Ln2sNnP0eu9j04c@Q|#hY-NY^4XkBugd!AK<)BmU(7U> zVE#{xbsf3gEj}_ku+374HQHh^u~Hi?EUl-dt0R4*CCY$hVv;ipMW*bqj5eJF$Gp;* z{-Od)%&yXi?b=}pGyMslJbxBX-wreRY1KyTUF2?okDU`&8$q4K5|O)WrzOl#3Wm!U zakAq@gttf3<~++)xojMKMUA5%9^wQDlOWh2*de5S;T^NmCuyGdX!m76ZW`|$BbR&$ zDXj1c;yeiX5b*2lv?QOHG@WnK<~;A)a?u3fV5vzEMWWMQ z7D;tS0nGhT;d5MY&ZEM$-SK9*-4lTTGnRsOJ%i+P2rnSKgzySN zKE_ZFKHZnnQc6dsJuRKO`B5owiHX38gAflP0m38*b_iHHZ9$pnwC80~H$N#2O?Uy^ zmzV<~4?;c!89uR3TT~Xhr7Z6jhx^kqXj|Z;ggA)fAtXSU1i=Qu4k2wxIk0WHq|MLE z0)`jB!O|}=hL8s#AA$rPoc2Zen3aA>DKE=KQ*S#yepxOa6hIa!fH)pP0)$BrY!K`a z(vtidru|&eKkZkGf0~cKeRE#E-LJ0e}Ai|B687F+Wew-cS^@s?$Zq!Z3l?$$4p6mRU(`;V?K@4JH5UcJzi zVKsE1S3fnz2C9bakQ3s$TqtFz?~(0CJ7W3lVd-WMsFzE4hG4z-R^jW6bNer2LuXu! zCJ9Y+pfm1ib;oO?=xS%&7JqwrSDZD*hW2X60r5~ylrBhyj(TH5GulFbPUsn_gc@|d z%L&#&Q(!-6l_H!SRV7iACB z3x(i+bdt0)X>Z%!#-!3UiYD3nK@ul#*wr&Y>4IcPX--C3Nk&>h1!*Oj(+co5bO-8& zdLZuCi;~~*q4>rkt&BxlN#?WyOEqMN9FPlQZyBYsI|XSar8S}k6~2-ShPOp@jO~z= zJ>$ZCorZKiz0kTS>gY7gkyfieL)C9uyfs5Do7N62)UD||O&6W zE3M;7l+f|sqn#;}B~2cFJkr#zl*!Y0wuPvbK#QPKXfd=5dI?$zvCndpE1)uHWuI*W z9qCGm8?Xe`N;WkOmWI;kenO`nsM0tdX<8JKY!TVg+8~Rr2wb#uh}P#vY^Ts-krBr4 z-m(Vix&EP^@6gR+@vT%Dq(jw19S%3Gk8nxv(#-{8u4D_+tvaw9-6$5@eDg&+$uA0- zwkGLj)9giJ1<561#(<4*PlOynhI2kNoOOY4hj}3~y2Q)&fH zLtcq9ScOsW5`~b>K~}K^ZG21kquvzhl)8%3u-8HS@s%TLw*?tsI~@Go?+tKP4W)Cm zeyzwGuocZk(8~{WAC-aM#_CP=26oq-hedrJl+<1!CeeyYoNg~u3e)rJ#3ZfBoH;vh zURoj9*9o_88+NIsMZ8HhR`HqCsLrL%foQouS5x3z(gj}XKp8joSA22tghwi|xy_|G zO*N+8s}zy+HCH*XNUeG|pgR8F>K9!7%YCm^S1I(`>6z1Knk>d#|8O(elv_|}&dr)P z({E~ElO@Y+o|kUQVbdgSR<6aIn{O%bJ7}ulU@R~iGc87=-!vU2vek+aBv&Eso@4ri zsk;W$!*tOBnzj+NgUQa6$8? z^nSI7qkS8(?SHfFB(GqC9pq#xXL^C@Fv!PnjZLRyP>KUo%e0ayoe3cwOB$v>n9jOD zW1Jw53zrKKuBS{U1h<~FOusRmcY+Kq&=#g8OlGG2OgbkkILx5#MB$e&9`JecN7Hcg+KZpxc&viMDpY~p|_%F9mA zF=qQsIWW;vJoeU282d&Y=mJxj2eU+Y(E2u$iRpKy2ByS%_?`2>FPUxO^|1Z93AP#a z;snVJ$U20plnd<&aua=Mdmw2v$&YHNYP0x8+8w0JD}i0eY87(WMVKS`SD%?a-72Q? p-^nvndMovHp>30V0&nN)ZEN2xQu}dZd-M2FQV-j&NJ@(s@(=g`%;f+8 diff --git a/PCB/OptoTest.SchDoc b/PCB/OptoTest.SchDoc index ba8391b544ea7d7774ffd7f5a782a66d5fecac7f..12f9541fc141855835a3a61dd9222ed65ea007d5 100644 GIT binary patch delta 3118 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 1,1 мм` | `2,0 мм` | `2,8 мм` | `3,7 мм` | + +Дополнительный запас к внешнему диаметру не добавляется. Глухие и скрытые Via +не изменяются. Также игнорируются неплакированные Pad и отверстия, у которых +внешний размер площадки равен диаметру отверстия, то есть отсутствует медное +кольцо. SMD Pad с нулевым размером отверстия не изменяются. Диаметр меди всегда +равен `Hole Size + 0,8 мм`. У сверлёных Pad задаются одинаковые X/Y-размеры меди +на Top, Mid и Bottom, но исходная форма площадки сохраняется. Готовые размеры распознаются +отдельно, поэтому скрипт можно запускать повторно без дальнейшего увеличения. + +Скрипт проверяет активное общее правило Clearance со Scope `All / All`. Для +каждой группы отдельное правило создаётся или обновляется только тогда, когда +общий clearance меньше требуемого зазора от края меди. Если общий clearance уже +достаточен, правило группы не создаётся, а ранее созданное скриптом правило +удаляется. + +Возможные отдельные правила: + +- `RIVET_03_RoutingIsolation` — зазор от края меди `0,25 мм`; +- `RIVET_11_RoutingIsolation` — зазор от края меди `0,30 мм`; +- `RIVET_GT11_RoutingIsolation` — зазор от края меди `0,45 мм`. + +Область действия первой стороны каждого правила автоматически выбирает Via и +plated Pad с соответствующей парой Hole Size/диаметр. Вторая сторона выбирает +только дорожки, дуги трассировки, медные Fill/Region и Polygon. Поэтому другие +Pad и Via могут располагаться ближе и продолжают использовать обычное правило +clearance платы, например `0,5 мм`. + +Например, при общем clearance `0,5 мм` он уже превышает все три требуемых +значения (`0,25 / 0,30 / 0,45 мм`), поэтому ни одно дополнительное правило не +понадобится. + +## Запуск + +1. Откройте `RivetViaConverter.PrjScr` в Altium Designer. +2. Откройте и сделайте активным нужный `PcbDoc`. +3. Выберите `File -> Run Script`. +4. Для проверки без изменений запустите `AnalyzeVias` — несмотря на историческое + имя процедуры, она проверяет и Via, и сверлёные Pad. +5. Для преобразования и синхронизации правил запустите `ConvertViasToRivets` — + она изменяет оба типа объектов. +6. Проверьте показанную сводку и нажмите `OK`. + +Все изменения формируются как одна операция Undo. Скрипт не сохраняет файл +автоматически. + +## После преобразования + +Запустите DRC: увеличенные площадки могут нарушить Clearance или выйти слишком +близко к контуру платы. Также проверьте настройки Solder Mask Expansion: медь +вокруг заклёпки должна быть открыта с обеих сторон платы. Сам скрипт настройки +паяльной маски и правила PCB не меняет. diff --git a/PCB/Scripts/RivetViaConverter/RivetViaConverter.PrjScr b/PCB/Scripts/RivetViaConverter/RivetViaConverter.PrjScr new file mode 100644 index 0000000..a95472f --- /dev/null +++ b/PCB/Scripts/RivetViaConverter/RivetViaConverter.PrjScr @@ -0,0 +1,8 @@ +[Design] +Version=1.0 +HierarchyMode=0 + +[Document1] +DocumentPath=RivetViaConverter.pas +AnnotationEnabled=1 + diff --git a/PCB/Scripts/RivetViaConverter/RivetViaConverter.pas b/PCB/Scripts/RivetViaConverter/RivetViaConverter.pas new file mode 100644 index 0000000..8e59c84 --- /dev/null +++ b/PCB/Scripts/RivetViaConverter/RivetViaConverter.pas @@ -0,0 +1,843 @@ +{ RivetViaConverter.pas + + Converts through-hole vias and drilled pads to the available PCB rivet sizes. + Source classification is based on the object's current hole diameter: + + <= 0.3 mm -> drill 0.9 mm, copper diameter 1.7 mm + <= 1.1 mm -> drill 1.3 mm, copper diameter 2.1 mm + > 1.1 mm -> drill 2.0 mm, copper diameter 2.8 mm + + Copper diameter is always drill diameter + 0.8 mm. + Existing complete hole/diameter target pairs are recognized before source + thresholds, making repeated runs safe (idempotent). +} + +Const + CLASS_NONE = 0; + CLASS_03 = 1; + CLASS_08 = 2; + CLASS_12 = 3; + CLASS_NOT_THROUGH = 4; + +Var + GTotalVias : Integer; + GChange03 : Integer; + GChange08 : Integer; + GChange12 : Integer; + GAlreadyReady : Integer; + GSkippedLarge : Integer; + GSkippedNotThru : Integer; + GFootprintVias : Integer; + GTotalDrilledPads : Integer; + GFootprintPads : Integer; + GSkippedNoRing : Integer; + GSkippedNonPlated : Integer; + GRulesCreated : Integer; + GRulesUpdated : Integer; + GRulesRemoved : Integer; + GRulesNotNeeded : Integer; + GDefaultClearance : TCoord; + GDefaultRuleFound : Boolean; + +Function CoordIsNear(A, B : TCoord) : Boolean; +Begin + Result := Abs(A - B) <= MMsToCoord(0.001); +End; + +Function IsThroughBoardVia(Via : IPCB_Via) : Boolean; +Begin + Result := (Via.LowLayer = eTopLayer) And + (Via.HighLayer = eBottomLayer); +End; + +Function ViaHasNoAnnularRing(Via : IPCB_Via) : Boolean; +Begin + Result := Via.Size <= Via.HoleSize + MMsToCoord(0.001); +End; + +Function ClassifyHole(Hole : TCoord; + Var TargetHole : TCoord; + Var TargetSize : TCoord) : Integer; +Begin + Result := CLASS_NONE; + TargetHole := 0; + TargetSize := 0; + + If Hole <= MMsToCoord(0.3) Then + Begin + Result := CLASS_03; + TargetHole := MMsToCoord(0.9); + TargetSize := MMsToCoord(1.7); + End + Else If Hole <= MMsToCoord(1.1) Then + Begin + Result := CLASS_08; + TargetHole := MMsToCoord(1.3); + TargetSize := MMsToCoord(2.1); + End + Else If Hole > MMsToCoord(1.1) Then + Begin + Result := CLASS_12; + TargetHole := MMsToCoord(2.0); + TargetSize := MMsToCoord(2.8); + End; +End; + +Function ViaMatchesKnownTemplate(Via : IPCB_Via) : Boolean; +Begin + Result := + (CoordIsNear(Via.HoleSize, MMsToCoord(0.9)) And + CoordIsNear(Via.Size, MMsToCoord(1.7))) Or + (CoordIsNear(Via.HoleSize, MMsToCoord(1.3)) And + CoordIsNear(Via.Size, MMsToCoord(2.1))) Or + (CoordIsNear(Via.HoleSize, MMsToCoord(2.0)) And + CoordIsNear(Via.Size, MMsToCoord(2.8))); +End; + +Function ClassifyVia(Via : IPCB_Via; + Var TargetHole : TCoord; + Var TargetSize : TCoord) : Integer; +Begin + If Not IsThroughBoardVia(Via) Then + Begin + TargetHole := 0; + TargetSize := 0; + Result := CLASS_NOT_THROUGH; + Exit; + End; + + { Migrate exact target pairs produced by older script versions without + moving them into a larger group based on their already enlarged drill. } + If CoordIsNear(Via.HoleSize, MMsToCoord(0.9)) And + CoordIsNear(Via.Size, MMsToCoord(2.0)) Then + Begin + TargetHole := MMsToCoord(0.9); + TargetSize := MMsToCoord(1.7); + Result := CLASS_03; + Exit; + End; + + If CoordIsNear(Via.HoleSize, MMsToCoord(1.3)) And + CoordIsNear(Via.Size, MMsToCoord(2.5)) Then + Begin + TargetHole := MMsToCoord(1.3); + TargetSize := MMsToCoord(2.1); + Result := CLASS_08; + Exit; + End; + + If CoordIsNear(Via.HoleSize, MMsToCoord(2.0)) And + CoordIsNear(Via.Size, MMsToCoord(3.5)) Then + Begin + TargetHole := MMsToCoord(2.0); + TargetSize := MMsToCoord(2.8); + Result := CLASS_12; + Exit; + End; + + Result := ClassifyHole(Via.HoleSize, TargetHole, TargetSize); +End; + +Procedure ResetCounts; +Begin + GTotalVias := 0; + GChange03 := 0; + GChange08 := 0; + GChange12 := 0; + GAlreadyReady := 0; + GSkippedLarge := 0; + GSkippedNotThru := 0; + GFootprintVias := 0; + GTotalDrilledPads := 0; + GFootprintPads := 0; + GSkippedNoRing := 0; + GSkippedNonPlated := 0; +End; + +Procedure AnalyzeOneVia(Via : IPCB_Via; IsInFootprint : Boolean); +Var + ViaClass : Integer; + TargetHole : TCoord; + TargetSize : TCoord; +Begin + Inc(GTotalVias); + If IsInFootprint Then + Inc(GFootprintVias); + + If ViaHasNoAnnularRing(Via) Then + Begin + Inc(GSkippedNoRing); + Exit; + End; + + ViaClass := ClassifyVia(Via, TargetHole, TargetSize); + + If ViaClass = CLASS_NOT_THROUGH Then + Inc(GSkippedNotThru) + Else If ViaClass = CLASS_NONE Then + Inc(GSkippedLarge) + Else If ViaMatchesKnownTemplate(Via) Then + Inc(GAlreadyReady) + Else If ViaClass = CLASS_03 Then + Inc(GChange03) + Else If ViaClass = CLASS_08 Then + Inc(GChange08) + Else If ViaClass = CLASS_12 Then + Inc(GChange12); +End; + +Function PadHasNoAnnularRing(Pad : IPCB_Pad) : Boolean; +Var + MaxSize : TCoord; +Begin + MaxSize := Pad.TopXSize; + If Pad.TopYSize > MaxSize Then MaxSize := Pad.TopYSize; + If Pad.MidXSize > MaxSize Then MaxSize := Pad.MidXSize; + If Pad.MidYSize > MaxSize Then MaxSize := Pad.MidYSize; + If Pad.BotXSize > MaxSize Then MaxSize := Pad.BotXSize; + If Pad.BotYSize > MaxSize Then MaxSize := Pad.BotYSize; + + Result := MaxSize <= Pad.HoleSize + MMsToCoord(0.001); +End; + +Function PadIsReady(Pad : IPCB_Pad; TargetHole, TargetSize : TCoord) : Boolean; +Begin + Result := CoordIsNear(Pad.HoleSize, TargetHole) And + CoordIsNear(Pad.TopXSize, TargetSize) And + CoordIsNear(Pad.TopYSize, TargetSize) And + CoordIsNear(Pad.MidXSize, TargetSize) And + CoordIsNear(Pad.MidYSize, TargetSize) And + CoordIsNear(Pad.BotXSize, TargetSize) And + CoordIsNear(Pad.BotYSize, TargetSize); +End; + +Function PadMatchesKnownTemplate(Pad : IPCB_Pad) : Boolean; +Begin + Result := + PadIsReady(Pad, MMsToCoord(0.9), MMsToCoord(1.7)) Or + PadIsReady(Pad, MMsToCoord(1.3), MMsToCoord(2.1)) Or + PadIsReady(Pad, MMsToCoord(2.0), MMsToCoord(2.8)); +End; + +Function ClassifyPad(Pad : IPCB_Pad; + Var TargetHole : TCoord; + Var TargetSize : TCoord) : Integer; +Begin + { Migrate exact target pairs produced by older script versions. } + If PadIsReady(Pad, MMsToCoord(0.9), MMsToCoord(2.0)) Then + Begin + TargetHole := MMsToCoord(0.9); + TargetSize := MMsToCoord(1.7); + Result := CLASS_03; + Exit; + End; + + If PadIsReady(Pad, MMsToCoord(1.3), MMsToCoord(2.5)) Then + Begin + TargetHole := MMsToCoord(1.3); + TargetSize := MMsToCoord(2.1); + Result := CLASS_08; + Exit; + End; + + If PadIsReady(Pad, MMsToCoord(2.0), MMsToCoord(3.5)) Then + Begin + TargetHole := MMsToCoord(2.0); + TargetSize := MMsToCoord(2.8); + Result := CLASS_12; + Exit; + End; + + Result := ClassifyHole(Pad.HoleSize, TargetHole, TargetSize); +End; + +Procedure AnalyzeOnePad(Pad : IPCB_Pad; IsInFootprint : Boolean); +Var + PadClass : Integer; + TargetHole : TCoord; + TargetSize : TCoord; +Begin + { HoleSize = 0 means an SMD pad. It must remain untouched. } + If Pad.HoleSize <= 0 Then + Exit; + + Inc(GTotalDrilledPads); + If IsInFootprint Then + Inc(GFootprintPads); + + If Not Pad.Plated Then + Begin + Inc(GSkippedNonPlated); + Exit; + End; + + If PadHasNoAnnularRing(Pad) Then + Begin + Inc(GSkippedNoRing); + Exit; + End; + + PadClass := ClassifyPad(Pad, TargetHole, TargetSize); + + If PadClass = CLASS_NONE Then + Inc(GSkippedLarge) + Else If PadMatchesKnownTemplate(Pad) Then + Inc(GAlreadyReady) + Else If PadClass = CLASS_03 Then + Inc(GChange03) + Else If PadClass = CLASS_08 Then + Inc(GChange08) + Else If PadClass = CLASS_12 Then + Inc(GChange12); +End; + +Procedure AnalyzeBoard(Board : IPCB_Board); +Var + Iterator : IPCB_BoardIterator; + ComponentIterator : IPCB_BoardIterator; + GroupIterator : IPCB_GroupIterator; + Via : IPCB_Via; + Pad : IPCB_Pad; + Component : IPCB_Component; +Begin + ResetCounts; + + { First process free vias placed directly on the board. } + Iterator := Board.BoardIterator_Create; + Iterator.AddFilter_ObjectSet(MkSet(eViaObject)); + Iterator.AddFilter_LayerSet(AllLayers); + Iterator.AddFilter_Method(eProcessAll); + + Try + Via := Iterator.FirstPCBObject; + While Via <> Nil Do + Begin + If Not Via.InComponent Then + AnalyzeOneVia(Via, False); + Via := Iterator.NextPCBObject; + End; + Finally + Board.BoardIterator_Destroy(Iterator); + End; + + { Process free drilled pads placed directly on the board. } + Iterator := Board.BoardIterator_Create; + Iterator.AddFilter_ObjectSet(MkSet(ePadObject)); + Iterator.AddFilter_LayerSet(AllLayers); + Iterator.AddFilter_Method(eProcessAll); + + Try + Pad := Iterator.FirstPCBObject; + While Pad <> Nil Do + Begin + If Not Pad.InComponent Then + AnalyzeOnePad(Pad, False); + Pad := Iterator.NextPCBObject; + End; + Finally + Board.BoardIterator_Destroy(Iterator); + End; + + { Board iterators do not expose component child primitives consistently + across Altium versions. Walk each footprint explicitly. } + ComponentIterator := Board.BoardIterator_Create; + ComponentIterator.AddFilter_ObjectSet(MkSet(eComponentObject)); + ComponentIterator.AddFilter_LayerSet(AllLayers); + ComponentIterator.AddFilter_Method(eProcessAll); + + Try + Component := ComponentIterator.FirstPCBObject; + While Component <> Nil Do + Begin + GroupIterator := Component.GroupIterator_Create; + GroupIterator.AddFilter_ObjectSet(MkSet(eViaObject)); + Try + Via := GroupIterator.FirstPCBObject; + While Via <> Nil Do + Begin + AnalyzeOneVia(Via, True); + Via := GroupIterator.NextPCBObject; + End; + Finally + Component.GroupIterator_Destroy(GroupIterator); + End; + + GroupIterator := Component.GroupIterator_Create; + GroupIterator.AddFilter_ObjectSet(MkSet(ePadObject)); + Try + Pad := GroupIterator.FirstPCBObject; + While Pad <> Nil Do + Begin + AnalyzeOnePad(Pad, True); + Pad := GroupIterator.NextPCBObject; + End; + Finally + Component.GroupIterator_Destroy(GroupIterator); + End; + + Component := ComponentIterator.NextPCBObject; + End; + Finally + Board.BoardIterator_Destroy(ComponentIterator); + End; +End; + +Function ChangesCount : Integer; +Begin + Result := GChange03 + GChange08 + GChange12; +End; + +Function BuildSummary : String; +Begin + Result := + 'Rivet hole analysis' + #13#10 + #13#10 + + 'All vias: ' + IntToStr(GTotalVias) + #13#10 + + 'Vias inside footprints: ' + IntToStr(GFootprintVias) + #13#10 + + 'All drilled pads: ' + IntToStr(GTotalDrilledPads) + #13#10 + + 'Drilled pads inside footprints: ' + IntToStr(GFootprintPads) + #13#10 + + 'Will set 1.7 / 0.9 mm: ' + IntToStr(GChange03) + #13#10 + + 'Will set 2.1 / 1.3 mm: ' + IntToStr(GChange08) + #13#10 + + 'Will set 2.8 / 2.0 mm: ' + IntToStr(GChange12) + #13#10 + + 'Already ready: ' + IntToStr(GAlreadyReady) + #13#10 + + 'Skipped, no annular ring: ' + IntToStr(GSkippedNoRing) + #13#10 + + 'Skipped, non-plated pad: ' + IntToStr(GSkippedNonPlated) + #13#10 + + 'Skipped, unsupported hole: ' + IntToStr(GSkippedLarge) + #13#10 + + 'Skipped, blind/buried via: ' + IntToStr(GSkippedNotThru); +End; + +Function ApplyOneVia(Via : IPCB_Via) : Boolean; +Var + ViaClass : Integer; + TargetHole : TCoord; + TargetSize : TCoord; +Begin + Result := False; + If ViaHasNoAnnularRing(Via) Then + Exit; + + If ViaMatchesKnownTemplate(Via) Then + Exit; + + ViaClass := ClassifyVia(Via, TargetHole, TargetSize); + + If (ViaClass >= CLASS_03) And (ViaClass <= CLASS_12) And + (Not (CoordIsNear(Via.HoleSize, TargetHole) And + CoordIsNear(Via.Size, TargetSize))) Then + Begin + PCBServer.SendMessageToRobots( + Via.I_ObjectAddress, + c_Broadcast, + PCBM_BeginModify, + c_NoEventData); + Try + Via.HoleSize := TargetHole; + Via.Size := TargetSize; + Finally + PCBServer.SendMessageToRobots( + Via.I_ObjectAddress, + c_Broadcast, + PCBM_EndModify, + c_NoEventData); + End; + Result := True; + End; +End; + +Function ApplyOnePad(Pad : IPCB_Pad) : Boolean; +Var + PadClass : Integer; + TargetHole : TCoord; + TargetSize : TCoord; +Begin + Result := False; + If Pad.HoleSize <= 0 Then + Exit; + + If Not Pad.Plated Then + Exit; + + If PadHasNoAnnularRing(Pad) Then + Exit; + + If PadMatchesKnownTemplate(Pad) Then + Exit; + + PadClass := ClassifyPad(Pad, TargetHole, TargetSize); + If (PadClass >= CLASS_03) And (PadClass <= CLASS_12) And + (Not PadIsReady(Pad, TargetHole, TargetSize)) Then + Begin + PCBServer.SendMessageToRobots( + Pad.I_ObjectAddress, + c_Broadcast, + PCBM_BeginModify, + c_NoEventData); + Try + Pad.HoleSize := TargetHole; + Pad.TopXSize := TargetSize; + Pad.TopYSize := TargetSize; + Pad.MidXSize := TargetSize; + Pad.MidYSize := TargetSize; + Pad.BotXSize := TargetSize; + Pad.BotYSize := TargetSize; + Finally + PCBServer.SendMessageToRobots( + Pad.I_ObjectAddress, + c_Broadcast, + PCBM_EndModify, + c_NoEventData); + End; + Result := True; + End; +End; + +Function FindIsolationRule(Board : IPCB_Board; + RuleName : String) : IPCB_ClearanceConstraint; +Var + Iterator : IPCB_BoardIterator; + Rule : IPCB_Rule; +Begin + Result := Nil; + Iterator := Board.BoardIterator_Create; + Iterator.AddFilter_ObjectSet(MkSet(eRuleObject)); + Iterator.AddFilter_LayerSet(AllLayers); + Iterator.AddFilter_Method(eProcessAll); + + Try + Rule := Iterator.FirstPCBObject; + While Rule <> Nil Do + Begin + If (Rule.RuleKind = eRule_Clearance) And + (Rule.Name = RuleName) Then + Begin + Result := Rule; + Exit; + End; + Rule := Iterator.NextPCBObject; + End; + Finally + Board.BoardIterator_Destroy(Iterator); + End; +End; + +Function ScopeIsAll(ScopeExpression : String) : Boolean; +Var + S : String; +Begin + S := UpperCase(Trim(ScopeExpression)); + Result := (S = 'ALL') Or (S = ''); +End; + +Function FindDefaultClearance(Board : IPCB_Board; + Var DefaultGap : TCoord) : Boolean; +Var + Iterator : IPCB_BoardIterator; + Rule : IPCB_Rule; + ClearanceRule : IPCB_ClearanceConstraint; +Begin + Result := False; + DefaultGap := 0; + + Iterator := Board.BoardIterator_Create; + Iterator.AddFilter_ObjectSet(MkSet(eRuleObject)); + Iterator.AddFilter_LayerSet(AllLayers); + Iterator.AddFilter_Method(eProcessAll); + + Try + { Rule iterators follow the board's rule order. The first enabled + All/All clearance rule is therefore the active default rule. } + Rule := Iterator.FirstPCBObject; + While Rule <> Nil Do + Begin + If (Rule.RuleKind = eRule_Clearance) And + Rule.DRCEnabled And + ScopeIsAll(Rule.Scope1Expression) And + ScopeIsAll(Rule.Scope2Expression) Then + Begin + ClearanceRule := Rule; + DefaultGap := ClearanceRule.Gap; + Result := True; + Exit; + End; + Rule := Iterator.NextPCBObject; + End; + Finally + Board.BoardIterator_Destroy(Iterator); + End; +End; + +Procedure RemoveIsolationRuleIfPresent(Board : IPCB_Board; + RuleName : String); +Var + Rule : IPCB_ClearanceConstraint; +Begin + Rule := FindIsolationRule(Board, RuleName); + If Rule <> Nil Then + Begin + Board.RemovePCBObject(Rule); + Inc(GRulesRemoved); + End; +End; + +Function BuildIsolationScope(HoleMM, CopperMM : String) : String; +Begin + Result := + '((IsVia And (AsMM(HoleSize) = ' + HoleMM + ') And ' + + '(AsMM(ViaDiameter) = ' + CopperMM + ')) Or ' + + '(IsPad And (PadIsPlated = ''True'') And ' + + '(AsMM(HoleSize) = ' + HoleMM + ') And ' + + '(AsMM(PadXSize_TopLayer) = ' + CopperMM + ') And ' + + '(AsMM(PadYSize_TopLayer) = ' + CopperMM + ')))'; +End; + +Procedure EnsureIsolationRule(Board : IPCB_Board; + RuleName : String; + HoleMM : String; + CopperMM : String; + IsolationGap : TCoord; + DefaultFound : Boolean; + DefaultGap : TCoord); +Var + Rule : IPCB_ClearanceConstraint; +Begin + If DefaultFound And (DefaultGap >= IsolationGap) Then + Begin + RemoveIsolationRuleIfPresent(Board, RuleName); + Inc(GRulesNotNeeded); + Exit; + End; + + Rule := FindIsolationRule(Board, RuleName); + + If Rule = Nil Then + Begin + Rule := PCBServer.PCBRuleFactory(eRule_Clearance); + PCBServer.SendMessageToRobots( + Rule.I_ObjectAddress, + c_Broadcast, + PCBM_BeginModify, + c_NoEventData); + Try + Rule.Name := RuleName; + Rule.Comment := + 'Rivet routing/polygon isolation. Pads and vias use normal clearance.'; + Rule.DRCEnabled := True; + Rule.Scope1Expression := BuildIsolationScope(HoleMM, CopperMM); + Rule.Scope2Expression := + 'IsTrack Or IsArc Or IsFill Or IsRegion Or InPolygon'; + Rule.Gap := IsolationGap; + Board.AddPCBObject(Rule); + Finally + PCBServer.SendMessageToRobots( + Rule.I_ObjectAddress, + c_Broadcast, + PCBM_EndModify, + c_NoEventData); + End; + Inc(GRulesCreated); + End + Else + Begin + PCBServer.SendMessageToRobots( + Rule.I_ObjectAddress, + c_Broadcast, + PCBM_BeginModify, + c_NoEventData); + Try + Rule.Comment := + 'Rivet routing/polygon isolation. Pads and vias use normal clearance.'; + Rule.DRCEnabled := True; + Rule.Scope1Expression := BuildIsolationScope(HoleMM, CopperMM); + Rule.Scope2Expression := + 'IsTrack Or IsArc Or IsFill Or IsRegion Or InPolygon'; + Rule.Gap := IsolationGap; + Finally + PCBServer.SendMessageToRobots( + Rule.I_ObjectAddress, + c_Broadcast, + PCBM_EndModify, + c_NoEventData); + End; + Inc(GRulesUpdated); + End; +End; + +Procedure EnsureIsolationRules(Board : IPCB_Board); +Begin + GRulesCreated := 0; + GRulesUpdated := 0; + GRulesRemoved := 0; + GRulesNotNeeded := 0; + GDefaultRuleFound := FindDefaultClearance(Board, GDefaultClearance); + + { Overall isolation diameters are 2.2, 2.7 and 3.7 mm. + Gap is measured from the copper edge, hence (zone - copper) / 2. } + EnsureIsolationRule(Board, 'RIVET_03_RoutingIsolation', + '0.9', '1.7', MMsToCoord(0.25), + GDefaultRuleFound, GDefaultClearance); + EnsureIsolationRule(Board, 'RIVET_11_RoutingIsolation', + '1.3', '2.1', MMsToCoord(0.30), + GDefaultRuleFound, GDefaultClearance); + EnsureIsolationRule(Board, 'RIVET_GT11_RoutingIsolation', + '2.0', '2.8', MMsToCoord(0.45), + GDefaultRuleFound, GDefaultClearance); +End; + +Function ApplyChanges(Board : IPCB_Board) : Integer; +Var + Iterator : IPCB_BoardIterator; + ComponentIterator : IPCB_BoardIterator; + GroupIterator : IPCB_GroupIterator; + Via : IPCB_Via; + Pad : IPCB_Pad; + Component : IPCB_Component; +Begin + Result := 0; + PCBServer.PreProcess; + Try + Iterator := Board.BoardIterator_Create; + Iterator.AddFilter_ObjectSet(MkSet(eViaObject)); + Iterator.AddFilter_LayerSet(AllLayers); + Iterator.AddFilter_Method(eProcessAll); + + Try + Via := Iterator.FirstPCBObject; + While Via <> Nil Do + Begin + If (Not Via.InComponent) And ApplyOneVia(Via) Then + Inc(Result); + Via := Iterator.NextPCBObject; + End; + Finally + Board.BoardIterator_Destroy(Iterator); + End; + + { Process free drilled pads. } + Iterator := Board.BoardIterator_Create; + Iterator.AddFilter_ObjectSet(MkSet(ePadObject)); + Iterator.AddFilter_LayerSet(AllLayers); + Iterator.AddFilter_Method(eProcessAll); + + Try + Pad := Iterator.FirstPCBObject; + While Pad <> Nil Do + Begin + If (Not Pad.InComponent) And ApplyOnePad(Pad) Then + Inc(Result); + Pad := Iterator.NextPCBObject; + End; + Finally + Board.BoardIterator_Destroy(Iterator); + End; + + { Process vias and drilled pads embedded in placed footprints. } + ComponentIterator := Board.BoardIterator_Create; + ComponentIterator.AddFilter_ObjectSet(MkSet(eComponentObject)); + ComponentIterator.AddFilter_LayerSet(AllLayers); + ComponentIterator.AddFilter_Method(eProcessAll); + + Try + Component := ComponentIterator.FirstPCBObject; + While Component <> Nil Do + Begin + GroupIterator := Component.GroupIterator_Create; + GroupIterator.AddFilter_ObjectSet(MkSet(eViaObject)); + Try + Via := GroupIterator.FirstPCBObject; + While Via <> Nil Do + Begin + If ApplyOneVia(Via) Then + Inc(Result); + Via := GroupIterator.NextPCBObject; + End; + Finally + Component.GroupIterator_Destroy(GroupIterator); + End; + + GroupIterator := Component.GroupIterator_Create; + GroupIterator.AddFilter_ObjectSet(MkSet(ePadObject)); + Try + Pad := GroupIterator.FirstPCBObject; + While Pad <> Nil Do + Begin + If ApplyOnePad(Pad) Then + Inc(Result); + Pad := GroupIterator.NextPCBObject; + End; + Finally + Component.GroupIterator_Destroy(GroupIterator); + End; + + Component := ComponentIterator.NextPCBObject; + End; + Finally + Board.BoardIterator_Destroy(ComponentIterator); + End; + + EnsureIsolationRules(Board); + Finally + PCBServer.PostProcess; + End; + + Client.SendMessage('PCB:Zoom', 'Action=Redraw', 255, Client.CurrentView); +End; + +Procedure AnalyzeVias; +Var + Board : IPCB_Board; +Begin + Board := PCBServer.GetCurrentPCBBoard; + If Board = Nil Then + Begin + ShowMessage('Open and focus a PCB document first.'); + Exit; + End; + + AnalyzeBoard(Board); + ShowMessage(BuildSummary); +End; + +Procedure ConvertViasToRivets; +Var + Board : IPCB_Board; + ChangedCount : Integer; + Prompt : String; + ResultMessage : String; +Begin + Board := PCBServer.GetCurrentPCBBoard; + If Board = Nil Then + Begin + ShowMessage('Open and focus a PCB document first.'); + Exit; + End; + + AnalyzeBoard(Board); + Prompt := BuildSummary + #13#10 + #13#10; + If ChangesCount = 0 Then + Prompt := Prompt + 'Object sizes are ready. Isolation rules will still be reconciled.' + + #13#10; + Prompt := Prompt + + 'Press OK to modify the board and reconcile isolation rules,' + + #13#10 + 'or Cancel to stop.'; + If MessageDlg(Prompt, mtConfirmation, mbOKCancel, 0) = mrCancel Then + Exit; + + ChangedCount := ApplyChanges(Board); + ResultMessage := + 'Done. Modified vias/pads: ' + IntToStr(ChangedCount) + + #13#10 + 'Isolation rules created: ' + IntToStr(GRulesCreated) + + #13#10 + 'Isolation rules updated: ' + IntToStr(GRulesUpdated) + + #13#10 + 'Isolation rules removed: ' + IntToStr(GRulesRemoved) + + #13#10 + 'Groups covered by default clearance: ' + + IntToStr(GRulesNotNeeded); + + If GDefaultRuleFound Then + ResultMessage := ResultMessage + #13#10 + + 'Default clearance: ' + FloatToStr(CoordToMMs(GDefaultClearance)) + ' mm' + Else + ResultMessage := ResultMessage + #13#10 + + 'Default All/All clearance rule was not found.'; + + ResultMessage := ResultMessage + #13#10 + + 'Use Undo to revert the complete operation.'; + ShowMessage(ResultMessage); +End;