From a4d0d450526c925c4bb9d86e50980edd00eeea4b Mon Sep 17 00:00:00 2001 From: fOuttaMyPaint Date: Sat, 18 Jul 2026 17:01:11 -0400 Subject: [PATCH] feat: add parent-inverse-orrery example (matrix_parent_inverse contract) Generated Blender code constantly writes `child.parent = pivot` and is surprised the child teleports, then reads `matrix_world` before the depsgraph has evaluated it. No existing example covers object parenting or the transform-update contract, so this one witnesses both with a brass orrery whose hierarchy is the subject: arms, planets, and a two-level moon parented with the keep-world idiom (matrix_parent_inverse = parent.matrix_world.inverted()), spun to closed-form orbit positions the check verifies exactly. The trap itself is asserted too - a bare-parented probe must jump, and the fix must restore its world position to within 1e-5. Runs check-only headless on Blender 4.5 LTS and 5.1 (both verified locally on 4.5.11 and 5.1.2); --output renders the gallery still. Co-Authored-By: Claude Fable 5 --- .cursor-plugin/plugin.json | 1 + .github/workflows/blender-smoke.yml | 11 + AGENTS.md | 4 +- CLAUDE.md | 6 +- README.md | 20 +- .../assets/parent-inverse-orrery-hero.webp | Bin 0 -> 28988 bytes docs/gallery/index.html | 13 + docs/gallery/parent-inverse-orrery/index.html | 591 ++++++++++++++++++ examples/gallery.json | 12 + examples/parent-inverse-orrery/README.md | 34 + .../parent_inverse_orrery.py | 347 ++++++++++ examples/parent-inverse-orrery/preview.webp | Bin 0 -> 23758 bytes 12 files changed, 1032 insertions(+), 7 deletions(-) create mode 100644 docs/gallery/assets/parent-inverse-orrery-hero.webp create mode 100644 docs/gallery/parent-inverse-orrery/index.html create mode 100644 examples/parent-inverse-orrery/README.md create mode 100644 examples/parent-inverse-orrery/parent_inverse_orrery.py create mode 100644 examples/parent-inverse-orrery/preview.webp diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index b5fcdfc..46742a1 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -68,6 +68,7 @@ "examples/driver-wave", "examples/gn-instance-grid", "examples/gn-sdf-remesh", + "examples/parent-inverse-orrery", "examples/shader-node-group", "examples/shape-key-blend", "examples/swatch-grid", diff --git a/.github/workflows/blender-smoke.yml b/.github/workflows/blender-smoke.yml index cc86777..0a65ca3 100644 --- a/.github/workflows/blender-smoke.yml +++ b/.github/workflows/blender-smoke.yml @@ -264,3 +264,14 @@ jobs: # failure. xvfb-run -a "$BLENDER" --background \ --python examples/color-attribute-wheel/color_attribute_wheel.py -- + + - name: Shipped example - parent inverse orrery (matrix_parent_inverse) + run: | + set -euo pipefail + # Frame-independent check only (no render): a data-API orrery whose arms, + # planets, and moon are parented with the keep-world idiom; asserts bare + # `.parent =` teleports a probe, matrix_parent_inverse restores it exactly, + # matrix_world is stale until view_layer.update(), and every orbit lands + # on its closed form. Exits non-zero on failure. + xvfb-run -a "$BLENDER" --background \ + --python examples/parent-inverse-orrery/parent_inverse_orrery.py -- diff --git a/AGENTS.md b/AGENTS.md index c1d4aa1..c659622 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ The content base (counts are CI-enforced against README.md and the manifest): - 2 templates: `extension-addon-template` for Extensions Platform add-ons, and `headless-batch-script-template` for unattended batch jobs. - 17 snippets covering canonical patterns. -- 15 examples under `examples//`: runnable scripts that assert a real +- 16 examples under `examples//`: runnable scripts that assert a real API contract with deterministic checks, exit non-zero on failure, and optionally render a still via `--output`. Each is executed headless on Blender 4.5 LTS and 5.1 by `blender-smoke.yml`; its render ships in the @@ -41,7 +41,7 @@ Blender-Developer-Tools/ rules/.mdc # 6 rule files templates// # 2 starter templates snippets/.py # 17 standalone Python snippets - examples// # 15 runnable smoke-gated examples (+ gallery.json) + examples// # 16 runnable smoke-gated examples (+ gallery.json) scripts/build_gallery.py # generates docs/gallery/ (stdlib only) scripts/site/ # vendored landing-page build (build_site.py + template) docs/gallery/ # committed generated gallery pages + hero assets diff --git a/CLAUDE.md b/CLAUDE.md index c22bc11..00685b3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,7 +19,7 @@ skills//SKILL.md - AI workflow definitions, 12 total rules/.mdc - Anti-pattern rules, 6 total templates// - Starter projects, 2 total snippets/.py - Standalone code patterns, 17 total -examples// - Runnable smoke-gated examples, 15 total (+ gallery.json) +examples// - Runnable smoke-gated examples, 16 total (+ gallery.json) scripts/build_gallery.py - Regenerates docs/gallery/ from gallery.json (stdlib only) scripts/site/ - Vendored landing-page build (Jinja2) docs/gallery/ - Committed generated gallery pages + hero renders @@ -80,11 +80,11 @@ v0.1.0: canonical object creation and deletion, depsgraph evaluated mesh, bmesh v0.2.0: Principled BSDF material, driver-with-custom-function via `driver_namespace`, application handler registration, shader node group with cross-version `interface` API, `foreach_get` bulk vertex read, version-branch skeleton, and USD export with `evaluation_mode='RENDER'`. -## Examples (15) +## Examples (16) Runnable scripts at `examples//`, each asserting a real API contract with deterministic checks (exit non-zero on failure) and optionally rendering a still via -`--output`. All fifteen run headless on Blender 4.5 LTS and 5.1 in `blender-smoke.yml`; +`--output`. All sixteen run headless on Blender 4.5 LTS and 5.1 in `blender-smoke.yml`; their renders ship in the site gallery at `docs/gallery/`. `examples/gallery.json` is the gallery's source of truth. When authoring a new one, copy the anatomy of `examples/bmesh-gear/` (script structure, README shape, dark-studio render recipe) and diff --git a/README.md b/README.md index 2808bec..6512c6a 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,14 @@

- 12 skills  •  6 rules  •  2 templates  •  17 snippets  •  15 examples + 12 skills  •  6 rules  •  2 templates  •  17 snippets  •  16 examples

--- ## Overview -This repository ships **12 skills, 6 rules, 2 templates, 17 snippets, and 15 runnable examples** for Blender Python development targeting Blender 5.1 (current stable) with Blender 4.5 LTS fallback support. +This repository ships **12 skills, 6 rules, 2 templates, 17 snippets, and 16 runnable examples** for Blender Python development targeting Blender 5.1 (current stable) with Blender 4.5 LTS fallback support. The content is consumed by AI coding agents (Cursor, Claude Code, any MCP-capable client) when working on Blender add-ons, geometry nodes scripts, batch pipelines, or animation tooling. There is no build step. Edit the markdown and Python files directly. @@ -264,6 +264,22 @@ HSV across face corners with `foreach_get`/`foreach_set`. Asserts the attribute is sized to loop count (not vertex count), is `active_color`, and that the shader `Attribute` node is actually linked to Base Color. + + + + +Parent inverse orrery: a brass tabletop orrery with a glowing sun, three planets on pivot arms inside brass orbit rings, and a silver moon, on a dark studio floor + + + +### [parent-inverse-orrery](examples/parent-inverse-orrery/) + +A brass orrery parented entirely through the data API — the keep-world idiom +`child.parent = pivot; child.matrix_parent_inverse = pivot.matrix_world.inverted()` +carries arms, planets, and a two-level moon through spinning pivots. Asserts bare +`.parent =` really teleports, `matrix_world` is stale until `view_layer.update()`, +and every orbit lands on its closed form. + diff --git a/docs/gallery/assets/parent-inverse-orrery-hero.webp b/docs/gallery/assets/parent-inverse-orrery-hero.webp new file mode 100644 index 0000000000000000000000000000000000000000..e9d3f1450ecab0f5f05626a9ea8c2417c3bcfefd GIT binary patch literal 28988 zcmV(nK=Qv*Nk&FAaR2~UMM6+kP&gncaR30Yv;v(0DgXu00zNSqi$o$Jq#`1b$pC-` z32AQs%V~cqzi!0Onff0JTu7OKe!E-D z%no|KqEyK8!T&#~O#WCF!+jm|ySwcJ@yE=c_I#}TPy1eL9{>B_{2%bYl%F!c;C|Hm zxcx2vL)=645B;z8-)!IXe|G)&|4{#*`HS!$`S1B3+5dhoOMmZuLqEFzod5sr;p*+^ zCHs-Z!&-fYlW758m zf876B`=#h#dGMb#iT~MVdsLMTu|BcX^sp=tBS+ z*W;H77JK$tJ4EB4Nd*XUm*2 zaH9u69=1&Tl)>_0-fHLj+>nS19i^3$^hJT%)_d8S3JyAM0M3mMceA^dF7nHYCC7&IwpS)xxqRSOf352$@;3h7w`LrgfQQx(<~jCL6u#M8FIjkcs%E_6pxREdyix z0Axs1z&cN-4*>uH9FEyZtX`(fgA_af;SX#obO|)d&)?@G#^g(cn35Ez#{1{j5ksxM z+WTf8tT)7pN_vt|oTd5hAAu-%V;zDn5=><9;T=e-n6p*WBt9KQ`CucNN_zlQu_fS> zZwWPq*v&H@*Tazb{>3CBZk1w*%4b&8ArSoGI8rVag16sBLb2*eY6uUM_sQi>W?M#{ zi>}wnfn4i?_}VdqgMm8aG*HDCv&b4%6S3d~3Xt_0ci_1$5@JeIt7h_|cyxDIkN;$I z5Nr`04r~q8w+wmR`Yq_H0Y!ltbTGWNgqH$QHU73o)IB56rFCOVq4mHE>$CejHHLK} za0fGyIg?S;1|=|e-1j)T+3)um2NdZ-%GSYai*gFBCTe_wtzvQ;Fd_*L8f32XXlS?J z^BMEUYp#ht1&%MKJu8j&$JZ>8@4@6p2EP-}n_~V@bBp{#@kHlu`>#$#-``w1WAJXV z%ldOpd2Rw0U|cn0;d=oxgbdX`=5&NJ(TlOH{84v*BJ7q!eBk7m-a~(j=Pyk=;H@+* z;E&UV4klYM>i<-evnyWF!P^}>J85v9v|!{QTF0YpcMvXuigpuwUld#O2r9kl`odwx zm(S0y7O-K}$dBZ)s_wQPBe3>&qnEO15M4i(?$Co|Z~(;q(~GfTO@VN7$7I(9HeV`k zS42Va%W*Q-%v9yyCiiQwXsV<-85ynY_JK@NvJ#2C;IP1Ie1;d33^`ndb)X% zDERO1g84h%qM+YmV{)E*PW>VS=UsB#El)q0yn#+Q4p8b$6u4o<{IlmE4$gWZT)Qfy zUkGkHqXayHRzz!tl7#W44mZ|mA`4T6~KU3m8 z@$W7mFjsFdZYsQYL57@;zPlU-YjFGjHIm*1r&Yloi09Oj=zVg$_<+5vidET?94LxE zkl|I4GeMfYSF!bnJAn|Q zl;WA&o7W>odW<6y5GoLnx?iE=LjoS-*q?C!hJMl9O0ubZHDVQfnE?`q4HDPVrN+u@ zdyle*DVO4#(1{70p#LAcJDw>3b5PNNsS^*mVas|Oyq=c>5zImp@o`Fp@AmAFHMF?Z z?Uuz>uaL32{hEYXvRu%^D0c3#M=xoo#1V1~3cmS~r0iZwLD;NstH5Q0AIh7PsG`;; zbGMA@9l&&{KDjV!?5I(kHQ)WJlg~TLbFAuV9OC~4#5^3W;7qXq7wDeEsUzvIL^=7H zJVE}i>~+Qd&I!{(lFDpEpEhgw}ZlZD2H&)@?lsSn1_@-a;L}`%sUeo zq8=EdZMnw6fN?h^S@r-aS(M0CB8MkE!;!2%2h-pSumIrnL^>3=fbsfIHWWB;=SnHrO)Q31&`|Rp4bIZ*g;n zmZN{b+U?9l)!&pF7mlGkO7!7VtP&5@_a$SndAm(-|LL1 z+#n1B1Z9trp1{i_Kle#ij@4xEs=&z*{#@_OAC#$UyX`OcF-a|jj!>Kko+L~rfl%>K zQZu|_3xPHaBfFnMg}Jl=wb$Ysnco7u>j@S4N69K0t6I?rfA?5v(jgLbxCNNNM`?rS z`LP5x@U2#A4o(U{-}%;I@1N)zcHG6Er}g2F5q24$~(>c&6=fFvPd!dTW zPLYfhnNVCl4>58@%ygaoL@Z#qrVk`UL%$0yzP?31i#ojLcs@6O!fLUjoo5^i z#QdFg0kY2XZ|YXJDmB9YB8*g31;W(2Cd-1*2^xs}pv5RB|0=g5_Fufg*9nkAlI|!! z2gTE>EWu2upCX72yk_}3x0>73??O;plL!58*MR_j)Z0?gP8yoVS? zmV1qOp`A@&9@vBqyn4=mZB87hWflU5SOfIdrO7k-iU++jl6gn}2CjK?Bo^$a%^QNP zxIl&D@s9q>WQ}sY1j8(J;rjRowy@*E%gjIqyWw=xavcLcOkLz>ZRT24^h3ekey-^d zY<=n7ke4q_NpXw)nt*ac!mg8ja^rjNJ_F7V_XBhEqgT2?wn}pW`Hr+|1|J=8lvN{j zAUPRSG9a$z`RL;@&#MqMt%(nu;(pN_yd~Sw=C{>2cBl$9szdX&cArQAKpq-+w7%A| zoNnIVMt~5-&w>U-o4ApvRZ`DiHA?+JJ3mxW;|MyujbLe!vzsZ74wH-5_0 z{4@{(U#Q0nh)g!v>iIjGB6F>LGLIawlZk=q3`KGN`0O5bD6-N3$Wb%!C)}~#hQz{E$1=mQI_V5Zi{T&ct7MfVpwgnUs9sCmn{ zW2X@wVx*fnq~3$T;bbo!ctD$l25+xO$XdKXnls=o!T$6$$KHZwl-aSQJqa;BTh+Tv zZT8n7);~+o4-@|0RK!oEeW%iJKQWuZm$Yfbke8;Dug$g<53$||9qP59yG1eSk7K^1 z7}^@m+gKCHz2YYTQu^11h0koz-p-LmeBB3>l#HG8kd$!Q>nW%%PH?b2(xHZvwiaQA z#A-dft3@uLoxxIz-kwudIBC=5iUEz>3&iLMaf^lYp!?TAwO&d&y(>w%zb&g%=WHq} zN%$RXHoC_Jh5Xru(?cB!Ix4&B^>Q> zaSj{oAzMT;i;Xlt+*n%x#LhXPZc#RdN6@;(%WA$Nq(Gw}X+;-E7?%Sy7ZQ$yDh2A7 zBnTs0O6?Xa%N^Am8@GzW@Va@a-eAX!yzW5vB=GMJ*6YGuXHnOBm%>C$mb=F4T3Jmw zX%1c2YF3b}Hv+*cOri|0w=vRTSQ?QU+zGDoN?00_T@CTTT-;!Nd(BUZ1r#x7HR~8tL9H3H`^@TgVp|K zy2kjqKrwvA=zY7+%VT+^D^`afDOxrY8YAVmNP6E<=u=MJ?1~|Nqfu*TvPd}szzvW$ zfAW-?>ylF~;C+&WwL+5$z{&x;I$2g9@-Ax=;cH9K(LK=kd%@CVP?5sBf11;Fsmr-p zYr(4_E^Yu734{c6;D=|R4dom-lcD}_51J+K?Gqbi%td^j>@;}5bg=Uh10X4+UMn)T zJtP8b7BG?@23G{~O~JM|fmtqzt4|La=5>md_P0Sr$>mWF0!-P^Pv;QOBg z)}6>P{Kf)%wOs6^t}QeX@*`8dR{lY|o_Jav)|XX6)WSeQ==c)`tA|vrz5NZuAp!6~ z1)t=+rC|{4SqP}TNJ;iC(R8^gCdM|w&7Plp2I_BuN?|zMP?*p_x$qpl|K9;z|8Ao* zzAQlfUMkS2s5zv|dfUK@cZ(fyMS&cdS}B71L%7F^AF0YWp)J}mZ>G~gFe_ariXD4c z529}^_22O)_W{l#aQgc@@$>0?{!-*qOw=q|!7 z0nC?SZ6yM;+pTfh*F1b8R2oPU(Zz!I`nb+H-kN2eC_Jt(b0oROx$6FX9p=Jk%#b|0 z09q`h2@g`fNMk2!PR%r_^Y!(xg8-U!D?QseqB3l&`9`#hTz|c&0LW};V-*?t_tk+( z@4v_dW-&PHTmDlP>*_DjA|wAzGUt3ElRFNgP@JH0}k18&FIt(q>}XW(#>I4sr% z38Oyk%KzhqUL|T*El^P=`$C4i^i}saGq~weQ3p5H-rB)OxT3e7u!*(==6Tr%ttCH@ zf=W4Ge#&aDdE)lXiY23?Yu5dX`(E0hr`R;Oe-?ykstF+ONgPD(SdtI3jErFZ3d+uC zJNLOvxLNe<0gKX&m&Uw=n!CP2xqR&o)+6~g;*nU*&=sUXxiVaa1*~KVXPKdr6+x?3l54vt!0zdj>cbj{<4wTS6SQOxF$=q2)c;n6aE~D{?D%_vc>WKioJ7J* z)?~nxps)yeY~0lUJ;m9vvj=uW-PK7RfXJY7xHyI}1}?2AV7WJR94m@*>@?`AINntt z%4I%3xp?*;V8Fl*IPDV^S`8Tcj;-{`%kadf6W}kPIW9#o$rVQvg-D|kniCiC{u%5n z;9FxEd?T}dP3Q^aiEmD&$MLc>w5Bw1DbZhmu_4HVH~E4=LV6{*jn@p_LmEjZll<>- zNQwnhxcP|Sllm1iyjU5_G&KzYno36u&OEf5n~OuF;Lgb&pbLNp*shK(?!%>pRycXK zyAlVWxL!iVBU455@EqivXUfVYIG;zQNa%-mB$4Qm1z*875XKI7MxhT@O_=I2k4{Ln z#^Q{Yr#MiF~oW2)flVk%Ow2f!dc@2RDEToffRhRc9hIj5Ys991ML zs`+m`lmm{-r6^$GT)!C32Nd(je&t`zGm$c5!fRogDjuBUL?6GYIB8hblzSlcV24@u zs%l7S1Z^iG!Dz>_1nAOC_oY#ug}yVyEe#(dmr0b7;j#szYKL?@aJHvhM+fIeacgp9 zPS}CNt<68q=A8O$crg72>bX|Rdm%3IXR6m>$&%C6wK2ypjK?maC@cN0rwXS}d>+S0 zqBpn=#uy-vij*8A0dV)L4ya=HZgCkM|y#1}W4uD{MhUCwyE z8D&;AHNyRtU5j@1TMCa4KN1u$!K(~ z_SV0P65usjGKybKwV$~yI?(a`2r4iO@aJGR1O3g&GnDyW6$13QK=jVf6`)F1xSrs> z0hjZ>mH}LfqZB65paA~-;G%+dQ*QtNr0vqST^__|>#(rz`3JaVX1`Bpv@V>nqRRZ( zMV^?6E_*5yRW)L7{2O83n49X{R6>5$(Vq5jDLu63Bu`3_1#CN+1<7PA(+*KVMk>JJ z))krpe|4v_bjgl@Y1bKA3e!!KwdW71oUZ@1}E z_@gWWxSi+>X>OO`nH4Ph{hj6jF#T3f1hCm#N)2HJCqR88r9!UF5gXr+jNOQ}^GvQ{ zE@Rb$9FW@EO{?I`Il#}8mPpet8ogRlcOb-V8_-cYHsa@~J;q)&J^l46p>JLbG_2jqq z-?)E~KsYlrG=L3o7Y``rn9QM3yB8I1>5Xqd)T6Uz_`lbL?ws4eG$ouN=V-z>NnMgw ziR;3b?iVHTmv^g>haxTo%5!oxm5Xm9@}I18Q(|GEHda)In!B{I@=nI~i3FaK-KYYN zV?MO?zyKorz@8;@HT1!TFaL_HcQ91-07t}C(W81DnO1+LX{Ka8yGSa(Y+|!1tD2tK z4g=CmO2@e2tqS3Wli)A+ zFjB-SI;h>Liyzsy?hE!`T_6BbR@S*>BbFM%J7{rYcAyD@^#xE|d(6Z=9ROz+F~F)C zA{{%fL@=}o2AiC<1hliGk_IaAGmaHkFa@4GB?2aJYQi;#Y5hoD#4a zQyZmqA)20v-6M!k_CdI)IJXp_m`AuEEI52jYuweov$3Pk+6Q?ZEZl`eSJr0cvu0SQ zLaI>kdxfm4sZ%3ekK_aTT$5CN(@pKA`{-;a$fTzh-d(_6L8M7^FTbc4+V@ovtW%4V zb234i9^V+(lMiqY)aR1<)Ubi2o4d0ahh3npdSD|s z_v0}=$qCuVDf9vgjl~nT7Lh*jxtrdP!_#AL5=x$YQ301(!`rYxAo*Em8+C(nku*BP zz4mNG5MZQV$TZ6Pkul7H_x{=CsoJqTAj@^luS(FCaS$zbP=4Rn_==wLlq?ebS~{}` z20!zjlr0w!b0C!HeF`PMP#J7me-1ACsCxj0QKZ)-bHi-_B~l*uOjWA6i2630DvPGM zt>})(sLkcPO4kKMjVoj8z-}l(@!edZ$s}b|aD@7r_F2ZF-!;o+Qmgu)jHp_JoHJ)) zYAY-JcU>3UEQxCL9LTa_o@%LCk}U&0`JB=Ab;m&)Q$8V!V9Y<4duJcxNtYf0ByPB} z=Hhc}dh+Cc{V;U>ESacYyj1x6ECJ{vds_4W{sqm-w-bGjrb6wqg7J?g z)uM1NGl-KnT*PwVX#jA~GVj_4Tkw+P>BF6OUV8^YElB^^g(OO$*I*>#!-sXV0jHE? zsVV~Wr`kiBlLPQ6h^IOK2HJ%3mnFm!KUP9&#)O?>(4EdlPxJb7kq>}m_Y&^1DyhJ+ zlX5}u6Tgq1{F+&C!ZevLqp&J`%B#D~Y)m;rI`By zObF_GfR%+f71ekfJ8^ps&2%k~pm1yqt=^P6)E8kozn671XXt5f)}?=WKop-BpPaZ! z$L=g4Mvu53nG8YAh3>EcihEjmHJiH@4I~cxI+za!hDSvXHxSZvMVxT4Jd?V z8}OU{558uCfBu4Dzqn!0V*bX9MRl3XIZ*V`B@^di<}gN(%%TZoj$Rc&5 zpITBFFx(E%tmXI?K!bjIoQSf}A7(r)Q{u$a|6&$eH~K-aOl7~i0=xxEGCNpO*w0R2 zKt7wI6&l?<8^Ha{M$$L|3_!#}@<`l}ogVYJOp4dethH@3dilbwSTMQvX9EOO^HqX0 z?4T&zFJ=b?=X|?Dgi$0L37Nl=Zef9Do|&b91`RDOP`=wZj_j13PRX2b!OFoZDY z6dRD1Wt~l>uEO$Y*)oNyrfCDMFXAfro&G!3Fj~;7O<%?U#boX9lhR`o?;BdW&0dfmf+7i|Kj|nSr%i284fsb@B+Jq!zjKw|4gB3o>l zdsrv$ZHJ<9-wVuUq2Lyq-5SAvFbvP?NifKpv<7zPI|>r5MONDLPCTeMmAVh%@%m>< zQ?mj84)?UFP~DH;~JQCd=^csNSk zZDdK=2Y|O2iBxNZ1UL_Gd1Oz4h^i<% zY>pXD^>Y33c=|_`PoZ)l9z~84M7`QJI9PL+KbdZ2?Tfhj*76`gJ^SS$6llssKQ&Iee8UGY+WGq_2`lKPp$R^%se9u)ewF zl)5MKTlMtBUzq$?hQkq{Ml=9GZ%6>}$=JDL&g-}QH{0B|^%I45PWJ6ax%1khG5|Tc-7%-u7^c za(!BDc2Wr)nSP}uC}hd@|0>E$X|L@2PN&l0*S3I7>-22zT%wisvZFeDZ9?YTqFURk zJq8VD>oW7KsQ>>tq+10XCN8}T8|34^<&Cc(88>xjAYUpUE{;*?B z`=^e8(rV*vx=~dof5madv0vB&s~xnqM0OptCt~Jpv$LYX&wm^egqXA}oNm>grQnQ<>Q8m;sV{`#xRmDJkhPmnQeE+x_-$4s;W4;bLz+Cl!N|=5Hdz?d zSDM!_A+@DNw1qTbJ#?W^Y7+|v!oro;S#|FoiuxXT0|_6{ygIZ04F05Jbn|Z12{%`# zrrd|noRHC^DpK$w2^CpR%MsWFk#A&QSt)&_vP|^7xsv~?+e=9UZ0y9N93A{NP;j@mI~)Qn>$7Pq;z1bNq+;inuGPinzej*U zL{7cb;+)g_Yyz$u8LIgy;)ze92{L25tg5N#X?KjT$i!%;7?25<%x^Pr@a})?ZD}6yXP_Yfo?15P znRJUsk*5*L*i%690sf+h`D00~RTf_r1g;&O8Kb`zbKo+8C|%l==%}@?F;E`7)j(Bg zfM*Zk1%sS68#Xe`Tx$2eUJ0uTDppP2Wm%ioFLVVb_yh6*tQ23oXrX}_PfWsx zW66Yl;m9-1V9#&61VMV6Qn$}ajzLslb5lyf8#$2wzxQ6McAW5H9-uz>{wtLH_V~jS zpZ#JQML(}44lTSte;e80Z0+OYJ8m~$6`c{muac;=_RO{kKo)lzYB;&R#n zBU5&tZ2>+}NYe8Ls*~@vZI#B>iWTes(pvH3kLxzI{91`nA`IU}L~ZbeD^wkj>1=pnA=AIjB;(yP}!zM*{#_O1dv zEX1i4&T!BXy^j$6(Qc-TJA3P|SYG;|L5H{KAo@T+&AJ^yEL+S|L9UHFuFg&DSB)52 z!@E1Ua4FGJ>Rzs(DczoL3*pzIq5G580C4oja`B~Iem2t$C zH95@5f~gIZ!8A43K?D7-!*&6?4`yGd^%WJ^YXVi8G;$ys$| zW!3eV4l_jns%t0lmF`qjj*d`dA}67!Yz8sL(jd7cg0FXHwR z4=x9sx9B2Hb7Az2#~oj^A0xMReGPEU3r?g#sk=2J3r7osZvMW27>jS!5+~I_d^h4y z-o04+z(hu#94FF@Clckji6MK87Oriw z6dxx}>&KoS7-9LgpIZT~wm5uSU%NV7K`EfEqf4i%%C5^nN`(n^T>uhgp9v_3j!5&e z$E#J9Nq;)(DhHD8*1G4cEe;_Ozw+9HAz?@uGaod0@#=)_=s&`lh%c=S$uYyhnxvqQ zmzMtDpiv2%dnBaod2VlS?Vd3Q8JYpQLS*y!7-3v#TQ3@-k;?xqB_^NBUK|P<@`EEW zI?@kcpHNajUxpb@vvAXiZ zEWyV<=NU0?h}GSBTwonmm!bR{Z%M1zs>aa0144mF;q*OCK8{}-h3vAiDF=Elt@Yq! zCUF40RuV348CZ&!xKiyxk-v=}9F~0ATmV7oIuZGYhZWAye5c%Bzu>C2K$|Ef4Y;iv z(!V-b=8ELbDg=nNKBp&jc|--EWlZ+kINhY2cR_^qn_71T*U%@R;~I4IB`#YqdgD{z zDz(M@rOsg7X&g80A50^jsl?N3IFR_Ti3&Ph9hBxw&wv=s4Ls@a#q0pR$U6z_X+LG^ zxX3^~OrjZUldwk{?^!egbgAEn2v6KJ1Sx8969hOS5WKGbbzcC!fO`}gg!F~!pQAgV z$*AXLk&*N%y$lkx;auL-MaSo6-UATN9BV|5_$O-^mI-fr@aqy z99O8c?&6TWY7YF}T#o+i{a*tm)@6#VHkL8pu<-}wS(&9er))Nu0%%lLS+1n0dz1;v zYF>@b>@9RZ9%6dQo`S;@(%W$;X6GO1Z$rk%WZOv!kxf?z+ie&U1?q&Psx*DGq+5Z^CY}} zZeE{-E&XPGgEZ6vV}siCKsztgY<=f~Q7vQH``i^>Z|WC2$|<(9{^dMP$n`Ze#$Z>Wjb z9n9j!jyK)rXz^$nCwacslDxdtwMk+5W)F{O2I2JpA98B;fC72RnQJE7M!uf7@CuA1 zcGs<%RHxv9tLOaqnrVT#E~Z?6vx2vwo`VMHfbYm5Nt1`n_EH4YlCQ;xkdJd65K z9W=IU=^i*pXz(Smh*bddLnI78+jdEXhfqxo&fPGx=%{hF&-_{4!1%%yM+XNb4Vz{t zKa|XG^IEVjE5#OD6K9}#aF9^hIe6Nq>xC$A%s%hz`{TKU5;QhF)?H>w?Kq(l1V&v#1duvU~q2DMgz~d zcqx1JkOyyB>d8zF=)t%f61C9482_D6`P7$Qr#x2NnW=h;(CL<10b3GCN1M>$vNL#W zf(7n#1ry*!kmBi0D4HNEff|j8&0ag4Jw;keWdWW&lR#1d1#Sczl0FAf$p%f!C%rjH zEsHkA`V-9FW6BeNMd{K}cH(<3RooOR|AF?j`Q%&dTe^}ZVx_d7R+tI!DHWSakpTl& z@WnQ*vUW6f-u_f21e%05VIx|sHXmVxW>9#_X~H(YBWg)*^IxNsF*EMjSrm?QA#|(L z=sNi#qq{Wr)l)T=+Vs&FJA#$(_L&y_d*lKh%qh*cUJcIi@hF?+2+aAM#v@#ncxSzcY?21*E z8o{eL$yYs$2xhuoiZ|tL{2opDajpJH(W^yUPZwx9ap~fICP8HFZ{|;^+a=Y)>P(@}Kf?)k}e5$?;Dp1b)0)zIl7Xg}o1NCa>tdOVY^9CZ3|xADvp@x|Lwi~TRs1_;W$!5r810oVMwGJY zPwmra#WMiNhW;bYEmpOx`6~>5lLs?81#sYnO*tCSYK8!wf%&k9CN|jbne~56nR1LL zM!B(^Sx0I9(qH)0c7MOc$&Hs6n^K`5O|Vem%1Wf_^`|HK#_b&?(k9$ zRloKup9+n2uY=;kI*Ug3s6yLrtH*g+Olnr%^y4aL+gRY+zldRW`b;iKs_jaaZe~|w zP)Ya9?LexLepfKajQT#gg174Bk2+`M4Bp_;^H5Zfi_gvf5f{!%$a%x1y(yT+XRW65 zzReV97wrgZ*+wdxY1#is*LPy`YLCTkbe#=R^$xwCh@K_o5k!}i240d>tJ5h9)y6x( zk>1ek-oVPF@t}R2W1xAEi@DQ;vNNn?xn4m%AMr3~U=8)6V<~J=J z&+nvp!#g@h0BZ?la#+$a=^xu-U76z9YUO8B1+cJKNwgg7>W1!LhQ5PG@90+GgWI3H zXkwL=-Vc1@?e4+B+s|`13L{TPLH=H!3H&^k40`yviFF*Np; z=X~p8Js}g$g-RO1o<#9pj)>TPfCmZX@F*x{xP~T{Zf;3%ZUY?dnycl%9zz&zstDzK zFekehWkiN^>pUvdIF(^La?v1MKL_q}ksxq)>6R@}nDTC)iY{1tZ2a6irM9=_$tnjh zJ4r;|<1Q<~dbPpw5ZK0aB`;`kbQ5&yyxco1D~yrzn={LZl@HI!26-yT=&#ji=v4X| zs4wD+iHy~g2d3~NX3InV1sf$v?^f9b71RWW+Mcl{ppb1iev27m-Wf)mZ4SF-NYiX1 z_G^rv35dk$Uy~tm)&)CrZQ|y9v$b*Wh~-wnzQBZ=3im1@eC_v(h;z&SLXpUbPa;Jd&bd1YaG2%rzRi^g4~HGmZfavJv~rQO5;ex+3U z)B?M0ND1z!uwab+f0}dM+*K%wzkIHlokPF8?kNv>bP$>W&mU%O6JfWE8J1RJwh*V} zdhR~?0H(`hHN>Zo0JYe?05JE>&Ys!HP$dELk-%L=t5JW%i$>A%AFVIvl|e~95%wIF zT?TRbGfk?hk?jSw<~nxq3c7D}f2guN_lP$*x%!M`sP+Da>)IB}vELBDWp4b|bS1I5-D9P8Rp9cx--Al{7$x>* zfjGZgmOZIY8L^a2aRT7`uu=PeN+f+WQ#@&TIb$iL1%xYdNYd?C#dCdUyWd4>4vO#W zy!{l4RKNO-v*A+8?y)TPuOoHCRdofz8bQ0 z8YVY)6(B4kB6uyaqVza{Tt=A@@ofG7q6XdfCsFgM{oi=c@CLIva7mSnAvi8)@Rmf< z_P`cVuI`994-Ac$Q3vYz>nQ5qsuM!s#j-F~q@Kx{XZjnQBT=mDKJI|=|4{S^s_jLB zE)*P@g?K9an~a5Wzr6mU~8PF1P|ruC>M ztcbpxWzN1ar*0>(LH%@yAdzz|KXlw?@%0`pDGhEY)DtA+)}Kmq`{R zDy;^b2qnw*Q-|C@L8AxQB}N#&E<%c|NwgM-aDfRuymmw^Y6>kw&rha+x4xJ~fBb-5 zqxMggJn7RhYS|SIJsZ3EB!N>LveIy+#I-m{;wFF@ZXa3j46;p4`aEtuI8-{_fxI+p zyKqy^6S5z@Y^XVYzb$71@1tFv#^JPNM$S@;jS22GPjgoqC;OmrXhRUAN8nJCM)(Xv zN)I8!U}I^Il`;~k6+C+X;`e3~_AIf$0wpH58wX z6D1U3R^eM)gEk0KbWDlBfs`4& zBXqcH0x!|ffa%IpN$w5A1 z&)n=}BoAVW!99jgQ6Km@zVRr>b5B=@Xq{MbYucD65I2y_;uBlVNEtLWu2D7Skt+!% zc&Z$-mil`jOK&QdE$i&9=PhHw5*9_Jv(60kIZiL!6-)*T3cdSWZBM8+Bl<=FzNqi} zPJB0k3p>kq4&}At{=O(*pOX(aUVAGOb$brzD>%2t4Ld_c(L{E1yQ;ga9{ZAru1#5*I ziI$P`NYpp>2k7SimWT~3?Q0@NKi*Y=qokwhlKDQbJOrwOLDsCjGyF@uAKO)+K5hPF z?h5M1!K6;Nip6d@C8Fj!u4?;-P83%NV8Vpm8CjwX-|6aE34=Tvf2fVp`fF=E`;J4E zRbZ%0ZY?b%z-TgFMr84{hE7FCnnQZwVG)s@agKS(d7${o%Gj&Rg7`s5y~JOi=y4tP zzmQT@;PvCIn<8<-mxd;mLH1X26rj%JV0N2ZzS>~5b_D7rYsn~O%Q!#p*9*vUwm}yH zC#b5*k*OdrR~TzL#P%Jmjmo2wLr-R|sw-*&j(g_m;jhcL?vFLnUW;;;&qbIhJF0mB z*1WI8--5WEG1Y52bDLu%*xE^t;Kr+h~a);0R&k&t{qSdX8&vo z>Wh&7G5oQAPLLs)dQ7KkH^HDCn9}@8ExzZ z^Y#j6L$h%J+Uq=+=fatB7ToD}PiZQ;JK*!GPD`*BR{B23Ois#=uS%m(I;jC>Q%OKs ze+$hc7ZkEISL*+65#pPMng$!O%H__)se>btgXRcX-+PtjbLwKj54fhah+P$x1!6)k zQGR=J(Pl}tPbLcXVC>iic9!11*TqgX5aKVboX@|~%X8Xs9`^WO^ptXgMAA$SJ?Rml z)%Vz|a@@a7pU6ZMCzXZ!U)_R4cz|SRZ`A9q#Qp!9Fim;H+c9Cr(dc`Zv+u<*PeH7X z<-!^Y2CrxG6O@4uc}YD%kK|@(UQSTAzW1*cRqB0~g5Xyve?m=>@%-bolhFtA5lh)|S+d;sE7$-5<1ABXqr7c<#iUp10fHRp2oP6R9U?3i)|b9WIi(IeX1OfMiLU zlf+9qYSyR+C&YHT*37mBI55CvUp8Zch2iQ{vDbk<9&Z4G;7auMnV0 zxa{n|t)Dm1nQ%5<>pMMKk!RA0rUrk>D0PigNkn@TEchO4q+-IV$w+mGKW`_GIxbewyldb-~9en{q9!(R`scE2_?Z(1`q=7Y;(CUY(Sr zD3va@8f(q3%$)Ccy$zHk>sf&+`1&Z;%-u`&_a89}u*uz?yMLQNu3x&B|25vNLa9Z0 zAkL{5va0nPYd-Tj%L*y6+M3J3tipu5^4Kf;XIqxaFg_iXh-=L|BBC2TS^v{GHEW&l z7@i}7h-`<9km@$fbbrIVIN<IX61YTuILi9Cv78F9J@wZ+spgQ zz0PbHdkRImCcE>&%3`oi`d)Nb?{x~Ksz($z^FAm_U`2AC1TdX4o_tIL<7>IRv@VDi z(%tt_m|E06=bTMeHrhC&t!(rl{r$LghkZ)H@ByUW6_v;+bB)90Fba)q(~SF!{^P(_ z>FO3SByLc8kF$5Di%gMliMgqur&infFV_LD#TYV5>S49V|wDzyPZs2l8i)-16MW9gmfF@|dI zf*2Ji*~IIIA3Qk|I?cC0f?Z9>0bqkCTSS^yTq145DPd=B@RQf$B5C+O=A<*MzpA)l%5H`}q--)|#asEJiVfvXO><590CPIEA!&h5O+*t4uRad| zys~-Fr%Zp}60AZ`2P7bc7U7q4G?9JYPkvldeVpVfDzhiBi zy-gOUB|0;jNq}FocsP-c9HpPo*DRMNHO@7}$3+awyVHZGWsi7|YP6wzZqMv|g{#r< zGO~l!u*P-SafLNXmNRUTSRt(6fC^8P*BN}4<+eHVfk1&xdry6E*35H zc^94}lW)jbv%(V_Gd3TrWthzr53D}BBWYHRQ~2wM7N+ZS*6FTv&$=qBa=~T7%vRrC z7JYb(K;z)CxAYu0W%NU90rlbZ-*k!~$l7->+mvPb-S+iC+ysQmd29Dd^DOW`OP(2X zy&TS@&Ecm7!Kq?`elN_RJlHe{Wwvhc?##K-NJ}nVYIGV>#(qR`XD(W*6l&Xg-^3W# z3tWsmBjD0o4jQc(^{rHA?JzL;EHf5b#G?cZWt72B+b{__JMh@Y$s_XT(d@r z;ANOqI3M`{4>G#bqXBlbAG=f#RlN=qQ`U_d_-|7;U;P#GSEIQb^oC?p;`HamF&HIU z8R-Ww+17Z*nCM5+zZ0@dm!t6_=A~-%?YZR~KpUjKQh=X0QLCb`6;5>RXhwe+>m|;S z-}NAFHabc^BM}BhhLPiK5$B-)`81|(F^vEWwhL&a( zjKk<#F@c?zm978Xwa+K*zSUbqKd$nz@H6NlQ@(FX+XmF>q!Mg4Fo3y!blM5rZ#|u?k_`nA% zJnjylPJODa&Nr^BZU1w2mYzOabyC0CbRmksgVd7x*|Iwa{?aG{(SSuxRmckjF+Hqs zT$GXiQ*#?Q)EAtUm5Jg{7z@wVaeneVMRACp6E3jz9{pa*#WhmP%_=qG>Mj7KNqCPl zn3W0+Qml``JB%#SdsLC!I>F7)hFoz}{<}#3;mIsWC^3plY)OgvO=JUu3H4}s+=Ee@ zuq|d~Vrsjwt1(4i!*`a>FM=;cE_R_E0WUGo&V}9zNL?{rnI5ysH^+;$@!9K?o4pyn zOr>AfoPu%6P1}!T<*73abNX-2^8nrQWTZsRLMWZcN%}CVap)i>*Y{5E`p$e zZA8@DA3J&en5N(`cLM!~BgO(w>WE@7u~y&)Vsuc3cRu)3|Bo5p&tp2{VUs|bwBL@7 zQN8|@(5qWc0_WN))w25+)}vW;p&aZ}6>byS0Xux(u_!lwbUs5<_>55RMt>%*?u&H- zNiog}^bqQd{+hfdNpgn2s>v>A%Xv)2?(v0zRlZ*jjVls_%-^!{pny#bEAfpex3!9k z5wgUKlM-I&Nk|j-_D3K8M5EX;p+__Ui1yQW7^K}xy{Kf{-z$50@iqL$&pD@D-=+6h zhKbbT&vheKe<3jq5}g;r&LwzpMd}>)^@5YM#Tcb|%PIyUtIAKp@V=)kYa37eQw5^` zZi!Ov6;6=5vI3%3yYQ_1mdFLY)cGwg?diYoS6DUa^uqAO^!;nyHSYH?+3Qp;56MN? z*kZ)J1(RXiinzc4EY?e8Cm}xzMire&`Y5dZZ`LyG*lxbZy#02!Ezm8x;!YPMk+OaH zYHcRk&-_PurDU_eHtY!%0Z%-eNu~-rkUl|cnPqY&;uh#HFigxRM+{2$2_k)_JXJ+@ z#9KSFxg`Islbl||;1d;SIn=iS*HLrC+1pAytQ>eyQXKKs_oM=ec{MN5faAD(*j|Q} z+v@MM$?r62SE&@nygI~Zl0aFWxYMhG&OW@unPF)+G6R!5*@QaaC}sHyev630Kfvs3 zNIoBeb&=ZhST%fR_}@6Jsm=F8+ijX>d_LUs-R&ZxNDccDixAwMr^J+n<&`p8J2qfg zdI3uDgU9RL-KzaXD5Sp)x;7F+Qu2It*D?(}nb@Aci|gx_Z+Krd;pO4OiKw#EmS zUH?rt;VNxYV1@E}sAE*fUwunF&1gW<-_;GMdb;!^lV7_NT_}O>{JO8XUFWbfrnYuND_7Stn_H`@~^H5p!hz zqshQqxLL6K?SjKN1G~B+I}?Yf2<9wn{h{gO;H89U=ZI%@>&9qa-Rh|{mF3+hOTxFh z4%*QZ{Eh8hxcHVUrKP{_Pgg2tZYy?SJXJUi!TYLfX5z>G6tp1|*J#*E#mi7Fs-92x z&=*_`F`!dQk^UfiVdu6w-{MY?d$J;K2ghp9uS$<<8kh`|N_zy&zSev(G$w)x@sK*Z zYxV%?OVCRyx)Y_TRyqN840U z8DEf&BtS#pg@o<$?JbNN+@#fW04yMLw5zSwnEYzHOfMBeH_y34f-aL}C&L@`EymKt zsZPeXg;gaH7w#IJx{OAMM*IX)BLSxxYygEn2q}%0`w*Jz6I6{56Am{jlG7{5%~Abk zbgGzr@}}*T7ijPn3dL;SF5Rddzh5Q~rU{m`_E=>-!d-C*{l4CCUNd61*LB;W5;hA% zOr5Jh83wwB%uX%jAo`LsRoP;7?kQgLc?SKtX$SV$aXQZ0wtVQBFqJ#h@U>xUPi0LF zHXe0$zs4zS*A`AGu%$c|#!kgJP`}+4Ksy+jiY~eZ!^0fHtPXeUF&~gsNsEBJ+Zjuf zf03=P3uUAQqYd~B_gE^h|hzg>)xX4B=3VRIWjV+*lLEIyCktjXP3VC(rPP)CW zQ5VIoA?-~ph?(S;u-Q}Z`=qK8Ub?)1~#LB;$Au`U^hQ+f`;r?wzkra50U;&1|*V<&vL$vnxb)jc)0y0*cP? zeN^AAN8W-^sxBJa_4)Ip=cc=ugT#Px)HWm6on;Bf;uLhAavRjv zN)V{IENQFQemN%&DI(**fdQtG|9VWSc;cijk4k?!(F!bA6cOxoE`Pn_J=HAw*53%; z7@iZkR2SRc8t+Vt??{4(UpQn2_nKmc#i;|lz9&2hav2T%5`1u$+6kX_)imdk82ku> zwgFfEM`?Xx@G5Z8>dRruSm2e&l}f55Rv-#Xa6alpP_^8m3Re$w#F%=?==E4VGO;Ps zj6%>rk)@7$7_p`Fr8HcE<;Bu1>6VI%U#BDfJcP0*f^zX1Z)rkC76o{0eTpDJ_%Kk^ z8i+R?HC_H(R!qm1TpcS>V_SjXpc(u~1BCnJpu03bJax*w?l zM3p?*57kf9mKat&lWWe*HVsb|PXTcs#p`u(zne6Fu~O$;Dm$k!{zq}b>_P~(>i`?Xmjcp!0Fwo10(qpsxS45Cwp)s#7H1bWlR@916g zcF?%$-~6^*8ml6SNIC#3;I$Lpm}mcge$5<3g^&L;t<*YK`RaF>E|@FdjF#W)sX9ao^^&ua+6D%2g=-rpPCx z7Mfm71~}aV$Vz#^Vm2%KojE#j#A)Gvc9TtO7&+Os&S+i}cZ!ClsdVfH+e|x3&BaW? zy>!d^p;w*o#{eX!OXGJ9E$=E3vou4N)jIcFWuZ8(UWhit47B;Hq2J8c-#0&4dU7V9 zZMwMXmOu|MhWRElGBb|T&{i)T(mWo!l0mzIEDVJ2#ww@SOTSldo_~BL4-8(+H~1k$ z18lh#`Qd4brOuAT%S_E9pf(k{c=##&6NY^>SHC> zO9~Ym2XBAsUexq7_i!uEMn_EovP8=uRlHnqPin-QV$4*t3he60@XS!3 z{o3)PT+Vl?oNUwA>>f=zTK-h8#{9n*sVx{8u@83>oZ-7P}>&X=Q+G_*yN zCfmxhzCNY!gmI3)rpi0$Ln=gJ?4^(l9nwRJI&<;S-`oM4s@gBq%XIBoPaMA9_Inqr z$ed7=EFXriI9_u|3o@@xxN5nP&PgXYi~{Y22}{9dZm_0%{nti&P)b2h?|_lq^|+W) zwSnKm-ZN~-P1#Z(O#76Ry^jbRKNKHrVc1}FVlzwd-gR451x)w6^7_J67Gu9jVh1p{ zCw*+*Vo#O^V-5HA&mYi!cHYa$=C#>M3j1km z;_%{+iF8JJgZF-;tQ+jhuLFkfi2toWcCn!u7&nRC%vdzV2%`0s` zRTTxosjTz@(1m9#u>%)0R$(Q4CnaP6}$crCO#@ADZS@w4OMOR z8ezwB160jY|eLn*8!%`$T^YeAzK;32li*gY&TSe2< zVaO0Op%Cd+=2eP$P02U7jS$`^eN)*nA_uvchXp}bfk8-n2q+g>(P(wnTYvchZ&H7R z>F>x^>p@)#J*g#WB)VA~knZcP9VaNo*xks+9)whrMd(u* zR+Z~mdHt0!6W3CT2VLzvi$KepBgzHLugqSKAA>57ip4!897C&;>ne75Lo5tVksTia$jJ}XBJYu$Lu$2u^|lIP6d ziQGT>V6@}xEAE3{+l7rAaX#Y=)mumCEK0(pr}%fnUi?& z8R#F5gxSRx=LMH*(OCf&s|U9@PHlWwbV3^{gW%eTSwo^&>TDo>NH|9}O`bd+90Oo< zsl%`%e5tm`TodzUkN9S;TZ-Q~miV_mJOHLby>Vkc$DFl*iX7-3Ah##Ghwu;3*Q`3} zAmWaso#}G?b$D*aR1>PJ6hoe}*kGxB{1J~lU}PU6R~mHFQqpGF817NQbr=Hf*o`Xv zueiq%{z&w2{$m)SsMDYq)AQYja2!METyZB;4}6q+^#J*-{a|=eiQVxxtFnzl64LFXQ^hR+?q} z01BBdAl3!1kpqS%&t)^_@Ezt?iM8-oq$drZ+Q8tD3VrW4_b+BIE<4m0^nA2wKhI}FNblK@Hs8Kk#rsRCxl_H5{!=>B<$6>^1;PEB zC_s4lx%mltz}(cS_iS&+b)ClZhJPC&-$!IgScqq)Pd!(S3NHi6EHd1T6YzSBqceL! zaVbX{Bs?%N6kvm8(p8%S%R)j{U>WKNtb6jJ+g)k4XojnjpW7Yv=#N2->jS%>LYkTH z*b{h0_T-Rr?TR51h4KePF!GeEMx_kuFPS_G$RI^ei!abt&~rx}bbC@piqKzK`$M3p z60NXHTZg;f%LI&w2aqG`5!8O;Nb zugw8Oh*Aod?)JAsPM%+dNX0*{>OcWExG%mVP4uf7Mr0iA^u%T#%0b<54=Bnw=B#<6cR zGCU#XS{)WBOV=rV)n_-@Pd5E8f_w8PD(?C6d~5K!vf&i9gSm^cU_te5ez9+E<6~3pL@{9^gE>7mPE1wT&JdL#W4neCiXvK!Vz5cDe3# zXl#6Q4^c_q(8RkegN$B0im2}ZF<*-j9T2$_>(~a@5~2G!OVww-L#Ig6?9z}iyGatVn4%_myn|nEpM}5V(0*qP@z=<$4ub_>_$nsou>jpAkWQT)HAtuH~=cTdCyNNEnUS~i|V!|vIxP$ca07ak!moaT% zr|l-pcJta$$$!O&fqCIi-kjWg)6|R23TzsSLyzg&6UPdq3fvlA53MXZ@#H#Kt9EV` zjEttV<`;jzJd`y$gyKybcfR>9#KE+F;k*Yzl#HBl3-cPusm0NDZu5b%JrH&Q=y$k$2FH{!*oC9eB$H$lvoUr466a~mQ|R~L8ofyfHfqVzYzIM73y6$U z`&7t`REfQbYJ1CJ{R=8MNb;yQLHy;d+xjz$1^12B_R2#G8M*k6|+3m;Er|&7N52X_0eduPc_Yzbg#$=JK-{*uF)_ewOnF_0$ z&RDK7EZgu(u>Q^~lU4S;G9P+Q*`O*@fIayJy|E5~<7DG~<~knr_S;dYex=#ruAfsf z4R!JOO_g7J zGLAdIhk2jw1*nuyf#i!3Qw2E-C9Fw-Kme9_8&n+muU^A;hC?t-F*CUN$G*XsZn^l| z;@3%hq=}I$AQc;`l{ zQqJXQNyh=qLWr=pSdaU$I#0a1mqq23IYeW}g*7EP*)%K%tLd&0q&>9V7mGWKhhx^n z<=B=COelh3-xy}`75}xy3&pX&x+VT;7$uODa+VLm0dT=)gy|FUalI3$r3#ARdpyU> z74su5P(W0kYK9lJS;RYcE=T|+rc@WgFn!?(!^^nk z`}}r5`2YCEx_Ib4-EO9Af}|O_wVm7EiooEvC+hI@1dHa-oQ<@y=f=AAh>gU|k4Ohb zPN2iQBtnW^T6rd)x7vI~;osk(qjh)jp(iPVVpV}Z`01@`*54l)!S#wq9D|#$ zm|sm@)bD3DKFz7nnL{91;5LEz3))D*)&5?u8hq1=(Atz31Qd^ZB$c#ZF!0J_jt|=+ zN~cxq)s`U7A!>w{Nd#f+v5`wDsjN&giMUA*G&4)jOXri9^Ge*6paoKjAJInxg=@T| zW{AVUZP)++0mRy!zyjaLGkKex+&cHCezoy?oRKr&-by_A zwL36SA(BC{HOY-Ln7YqM|4?IwUqej!(4X-(YK-%Q?!`#oWAM-bGgPh!P065rO+|Cz zcX}1Pe*cCO$QacqK1ME@_8%<}aC0{bnSuv2h-wXV#7uj}E>E7tcEf6tfyf5?ro|2E){O=GgYB3iWuol?Sd4r^g~@AK zLOvrn3DmCL*6xf(MF8PoCtS=J{9&MqY%fON_(b)WhPoOw0q6zaCLbToOIFkDM>N97 zkR|&#n;YAwt?cI5EGsgqP{iPYkrf$TWo|zOAoKm1^C958BwGB>%3xp9~kbI*w zDBMU?MjY=$dx#J4TYVl9gD#(>8r0rwJ%N~SP8(!&|28i0t~gUr=rPzN>Xf|)SvNyc6t$Q7-Mon0O}Hc_q*y4& zlPB*_2>uzr>XO(R!wAj=xW~9kgtAcQ5(+ra+pD^`>4@#`O!(3#4T-?;@3Ju5OlzXz z512^{7^)|z{xr8v4G>-h<>@u6S+ANjy@ybz`&cQ+>r=mk$s2Dgs*cQl>WV<&Y|N3V zz*gab`yat>X}iv|5!pU7gK+1|M>wKA0poHt>-sN-#5I#9!Df`=noE=cL#0ww`4$J* zD!%RffUWp@ce8=Heu~DgqTfE|0Bx@=OTkg@zj#oVuwbf(!x%PXKXJ2GZP-MN{V~;2 zc-f(JTHq7*UZqkE>?aTSr|&}@$|@U+1mzFpPDD%M(kXu5Ri6vyT|evY+aT6);xOF! z3%74(1^F5sA4bpCbw_n=4Ty5x0kStvZsiDZXW}azfQ}xMSeLSP2;*lWm`|k1a%6NG z)To46WHK`pB@8rXeC}QjB^r4+2#Gy_iH)RLcov3bsq5x5wC&D{BfC@kv8SlrD*9cD z&KM6_R@Fr}?aA5{U&-%R-g{f4BD8%;;RW7Liq7bF<73ele>u%`BG916^}d$1rEWl? zq`9(DfS1rp&bhg%Oa3Tgb4xCd5;*KlZa*tqCytKIscf3$FYiUsS|MVVn7S|av7?YG z?khX8dbt8smkjEN-~J9=1x2hh;OqlWq+uC^)KG8RzHY`vK4Mb$^Sk?hEjE z=p@$TY*{0LHm@rt+3(qm98*m!KHsfXP0rZ|njrcI3iq!Y$Vv7W)R}CYj3kUDI>5<->QkP?E9)+I-4yhH8$vdC{*hL zUN`Rw%xUnxR|2VDOlZk^8vBp9%;LVM&6q;3D|#=y_%>tEgjQ>@jvWc1sl7H!BQ@*K z4>7$n!!Xn-J?2NkMSoz26_bMzOh-pvOa{)eArnG8*wK~-I= zp?{M}y8LD4n%*7QEHT+}9&ZSv$9RE(fQ=st{$kiJ$x+Q`83@R2VU$8-+0@~3YQkYn z>$e-I_0cAI$y(D9eZ;y_eO{Ncw`;t}-@pOAijWSc%XTM>fcw=y z_=zWOk-^v5_*u>^cCwK-Ym?1VXc*-%MVrnOWHRp_) z6~vAq%CcFRCPoRC8|7KZY8NpCccI>u&4VBl0{Hun#-tMTA%j|b^PK@+pRM}Wq$y__ zfeP(-#%l&PJvaX}?!qZ){k@RIKOo*7L&ykIiOqMfj9JvyLCHu#?F0@4{zhT0Ygx0>Tnocw zJ6|!zd@sUQ`wPYIk0q(Bm`omU zK;&5^03yq1tQgB{(~cw;fSIz+wQUU%$?dXHSSGJ4!*67qR;9}F2Ko^j@Ii1YfO(zL z$Y`5i+y*3E(4-skt^lbQbhwDc$kfaHQCpQTl5I$UpZRY>*GqiTk)@uC6$ea3O>AZ3 zm(Ld-iG6rbJ`E2E4X-4li_cK{KwSdKYT!4@jUT$lKKhta>vAk@3DiHM-Se(%AgNdVvM5Tq(w(0DnKx*Tts{*kT?em@++E2M)tNi>UgJ$liG-j`s@%uD7jQ z#nTip>55L&K7ZvKRYHoMr8!RGIa;{i#6GOF{sGv7-RY?84xg``f=qf17KU!s&8X$* zoma?ER?2LElx<@HY%(nV2}sikOLDy5d*882q}|?U;KSpJ!tG{D`4RYpo{hKO(Z}v1 zwx-7Uh~r%bF7~GosZB-GxONx}z5_MU><&Z^;&PlUKyZd{2o9l7@)MvlI8>&3RU?eg zK@Ap=VjW-@oLQC^(>5Z;(Ou0afZ}gK&Iwv10FOO-I@VYTabZ|`#pPMjBgjhzv`oN( zuIN-_q$F#L&{PK>$oaVjkm(?8c@l~kxYS$CO<|$ziaV%k8;4Np$Ptv5Mv70SDdAH% zyZG}O_>^3xhVL~1L!WAoTElAxYKcm2N;ETAHY^f{ywiEKAAvELGEv^{b(;_dE2HS&nf0CqiS~(Gcr#cP)v@|>zBKM z{amaYfFd$Bb5>L6nh>bQk8H=@V844r9rbhiG-j`Ghk;hLzA|+uM8UR*rC*P%ZIDN|>#T`pn zBLCd!sM5aw3A^IrH+|Hf$*6Hb>Juzbj^M=Ja@u!G^4qdd#Wlf;oF)(QtKx`fN%2J> z7Wuk)KIK+DnlE8P`i@h>bakJAVidVv_?)L=B@oi5bfR@e2xC+|ON(lk2-dNMMXuP2Nmo-?Sp@jOC_+{vQy!~ zHOC(~mK2sRH>iPQok6hFw_u7aWe}yR?%LqXc_5wUT)@G*)3VZ7jy4_s&PAmX6}s%Wy~RDU%7)Bl_ zwIf-fWZ0vZ({4rMJybJuu|>EGZ9Q*Fq5R7&jy%^DtkF`rwDM0`v1(QtXGYTUyr_Uj zy>__?TSM_*-ns2*@SD#l(`5>N)^2;F0el*$W0#BK`)=-atiq-iB7ks?bvPtIRdz>P z1z!(i{!-r{t0N;A>1}!mq-qP<%muKKV5OxBqo{5PqYn7pe+8CS{Mz_SCIt85VS%Pz zz_bMe?HxpX$;pC!{4nl+Its1G5WO!hkTshmeIL%a$(nr1<_P7CgFl6_n>~@wqK35Vx76dNtP0!Eb0T;2nUuEznOusK z2X1fGjWB-1@>T2+vo*h@S@6KQA~T4#nCW_DTR z=H=p{C7w&9WY&nUwiv+QwULkdt46Tg&(+BzC>!KpfLV(1m^}z!zsK-sx@nfjuo=>z zH56iJFaYc;#_6-_*C0-quv+3zgv2g8DGmkrq+fo|r3TQ_j@*4|h=TbF&tmHETH}{# zw1z_WRw$WU+Td4KyJ2WXHx-mx(sYwu!vO8YkEK}(YgHc*Vns z9yQKu{MBY+PLd@cceVgXoj!XW+B(3=e zObW0`3Ftp?shtuiYyp_6;=H7D-V8lxLUj`-&c?4BCAIXFDzbTo(o#jW0x2YlV1A}U zY#$bHM-3+2iur{#1^6}+`kS^fChn&pOZCtBRMwt8EIcBRYZ*1#)Mc`(H{Z2HC3Qk8 z_%3W6%*fQM?T`Z_j1(@M*Is>b1FdSdpfTVK%g??pD5PgXI~v#P03t=Pgv5W-gzR1* zdMZeXDDjm>f3*xq3A#06tIEilQ{dI5Eo@vKZzr5W3$k~g>k;orBE%u2Q`maJ$H@4rX%uIl7#vZTD^;G@;3JDIgPWr2o z7^~15N~&>@=!v>=lnWU0?dePJ(Djp5rgrt8xMlzP_pup|0t4u(!9c81*s;! z`4SS5f-u>%eqc`pNbQd*A%2r>OU29UeFrA3v+!1Bo(>bC6*0 z(#CmVzu6P)%OXL;odteVR;Ci4YwE^KuC_bLD5*Rs5ys{xV*DQ zJ1{C^i#x<$1faWMW!d$;AV6~>&8ucg0JeA}AHWa&^5hR=9y|LJG4BAfJ?{&n3rT|C zE`9u}dh(cL8KD{#ZCyo;hTR5J>eXle;)4VP|X))BdjT@_MiF zfnLlXsKX%zhsp4)0YSF;%{$i|MIIyDEAaFZn)D+w3mBw7D(a3(@?-ck#}f_AnKklm zn2D!pBr5g3XC?CEk!UOD$N%+0Ax&*tv82=TAiToF*rDBvt>QK^NZ8{vWwba#{mD8W z2k{*dB8Uj}(ydF?)MCkBtgZW-RI`gwJv#seSjPA^3?FwES0^@w%;USV7oE0Examples Gallery + +
+
+ + parent-inverse-orrery — Data-API parenting for a brass orrery — the keep-world idiom (child + +
+

parent-inverse-orrery

+

Data-API parenting for a brass orrery — the keep-world idiom (child.parent = pivot; child.matrix_parent_inverse = pivot.matrix_world.inverted()) carrying arms, planets, and a two-level moon through spinning pivots.

+

witnesses Bare `.parent =` really does teleport the child; the idiom restores world position exactly; matrix_world stays stale until view_layer.update(); every orbit lands on its closed form.

+ View example +
+