From 1d75c8a91217d2e9ff9d8b877a5e04f2f63afb00 Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Thu, 22 Aug 2013 00:31:52 -0700 Subject: [PATCH] Update mappings for all objects, assuming wiring in Alex's brain --- SugarCubes.pde | 5 +- TestPatterns.pde | 69 ++++++++++++++++++++++++ _Mappings.pde | 54 +++++++++++-------- _Overlay.pde | 15 ++++-- _PandaDriver.pde | 136 ++++++++++++++++++++++++++++++++--------------- code/GLucose.jar | Bin 25732 -> 25782 bytes 6 files changed, 209 insertions(+), 70 deletions(-) diff --git a/SugarCubes.pde b/SugarCubes.pde index ab81e30..637f7c6 100644 --- a/SugarCubes.pde +++ b/SugarCubes.pde @@ -25,7 +25,7 @@ LXPattern[] patterns(GLucose glucose) { return new LXPattern[] { - + // Slee new Swarm(glucose), new SpaceTime(glucose), @@ -82,6 +82,9 @@ LXPattern[] patterns(GLucose glucose) { new TestTowerPattern(glucose), new TestProjectionPattern(glucose), new TestStripPattern(glucose), + new TestBassMapping(glucose), + new TestFloorMapping(glucose), + new TestSpeakerMapping(glucose), // new TestHuePattern(glucose), // new TestXPattern(glucose), // new TestYPattern(glucose), diff --git a/TestPatterns.pde b/TestPatterns.pde index e47651a..706626d 100644 --- a/TestPatterns.pde +++ b/TestPatterns.pde @@ -5,6 +5,75 @@ abstract class TestPattern extends SCPattern { } } +class TestSpeakerMapping extends TestPattern { + TestSpeakerMapping(GLucose glucose) { + super(glucose); + } + + public void run(int deltaMs) { + int h = 0; + for (Speaker speaker : model.speakers) { + for (Strip strip : speaker.strips) { + float b = 100; + for (Point p : strip.points) { + colors[p.index] = color(h % 360, 100, b); + b = max(0, b - 10); + } + h += 70; + } + } + } + +} + +class TestBassMapping extends TestPattern { + TestBassMapping(GLucose glucose) { + super(glucose); + } + + public void run(int deltaMs) { + int[] strips = { 2, 1, 0, 3, 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6 }; + int h = 0; + for (int si : strips) { + float b = 100; + for (Point p : model.bassBox.strips.get(si).points) { + colors[p.index] = color(h % 360, 100, b); + b = max(0, b - 10); + } + h += 70; + } + } +} + +class TestFloorMapping extends TestPattern { + TestFloorMapping(GLucose glucose) { + super(glucose); + } + + public void run(int deltaMs) { + int[] strutIndices = {6, 5, 4, 3, 2, 1, 0, 7}; + int h = 0; + for (int si : strutIndices) { + float b = 100; + for (Point p : model.bassBox.struts.get(si).points) { + colors[p.index] = color(h % 360, 100, b); + b = max(0, b - 10); + } + h += 50; + } + int[] floorIndices = {0, 1, 2, 3}; + h = 0; + for (int fi : floorIndices) { + float b = 100; + for (Point p : model.boothFloor.strips.get(fi).points) { + colors[p.index] = color(h, 100, b); + b = max(0, b - 3); + } + h += 90; + } + } +} + class TestStripPattern extends TestPattern { SinLFO d = new SinLFO(4, 40, 4000); diff --git a/_Mappings.pde b/_Mappings.pde index 73e1fe2..91bd543 100644 --- a/_Mappings.pde +++ b/_Mappings.pde @@ -107,33 +107,43 @@ public Model buildModel() { } public PandaMapping[] buildPandaList() { + final int LEFT_SPEAKER = 0; + final int RIGHT_SPEAKER = 1; + return new PandaMapping[] { new PandaMapping( "10.200.1.29", new ChannelMapping[] { - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), - new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 4, 3 }), + new ChannelMapping(), + new ChannelMapping(), + new ChannelMapping(), + new ChannelMapping(), + new ChannelMapping(), + new ChannelMapping(), + new ChannelMapping(), + new ChannelMapping(ChannelMapping.MODE_BASS), + new ChannelMapping(ChannelMapping.MODE_STRUTS_AND_FLOOR), + new ChannelMapping(ChannelMapping.MODE_SPEAKER, LEFT_SPEAKER), + new ChannelMapping(ChannelMapping.MODE_SPEAKER, RIGHT_SPEAKER), }), new PandaMapping( "10.200.1.28", new ChannelMapping[] { - new ChannelMapping(ChannelMapping.MODE_BASS), - new ChannelMapping(ChannelMapping.MODE_FLOOR), - new ChannelMapping(ChannelMapping.MODE_SPEAKER, 0), - new ChannelMapping(ChannelMapping.MODE_SPEAKER, 1), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), + new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }), }), }; } @@ -221,7 +231,7 @@ class ChannelMapping { public static final int MODE_CUBES = 1; public static final int MODE_BASS = 2; public static final int MODE_SPEAKER = 3; - public static final int MODE_FLOOR = 4; + public static final int MODE_STRUTS_AND_FLOOR = 4; public static final int MODE_INVALID = 5; public static final int NO_OBJECT = -1; @@ -253,7 +263,7 @@ class ChannelMapping { if (speakerIndex < 0 || speakerIndex >= glucose.model.speakers.size()) { throw new RuntimeException("Invalid speaker channel mapping: " + speakerIndex); } - } else if ((mode == MODE_FLOOR) || (mode == MODE_BASS) || (mode == MODE_NULL)) { + } else if ((mode == MODE_STRUTS_AND_FLOOR) || (mode == MODE_BASS) || (mode == MODE_NULL)) { if (rawObjectIndices.length > 0) { throw new RuntimeException("Bass/floor/null mappings cannot specify object indices"); } diff --git a/_Overlay.pde b/_Overlay.pde index 50c037c..b071128 100644 --- a/_Overlay.pde +++ b/_Overlay.pde @@ -824,7 +824,7 @@ class DebugUI { case ChannelMapping.MODE_SPEAKER: drawNumBox(xPos, yPos, "S" + channel.objectIndices[0], debugState[channelNum][1]); break; - case ChannelMapping.MODE_FLOOR: + case ChannelMapping.MODE_STRUTS_AND_FLOOR: drawNumBox(xPos, yPos, "F", debugState[channelNum][1]); break; case ChannelMapping.MODE_NULL: @@ -903,19 +903,26 @@ class DebugUI { state = debugState[channelIndex][1]; if (state != DEBUG_STATE_ANIM) { color debugColor = (state == DEBUG_STATE_WHITE) ? white : off; - for (Point p : glucose.model.bassBox.points) { - colors[p.index] = debugColor; + for (Strip s : glucose.model.bassBox.boxStrips) { + for (Point p : s.points) { + colors[p.index] = debugColor; + } } } break; - case ChannelMapping.MODE_FLOOR: + case ChannelMapping.MODE_STRUTS_AND_FLOOR: state = debugState[channelIndex][1]; if (state != DEBUG_STATE_ANIM) { color debugColor = (state == DEBUG_STATE_WHITE) ? white : off; for (Point p : glucose.model.boothFloor.points) { colors[p.index] = debugColor; } + for (Strip s : glucose.model.bassBox.struts) { + for (Point p : s.points) { + colors[p.index] = debugColor; + } + } } break; diff --git a/_PandaDriver.pde b/_PandaDriver.pde index cd85e9b..d6cd995 100644 --- a/_PandaDriver.pde +++ b/_PandaDriver.pde @@ -66,55 +66,73 @@ public static class PandaDriver { }; private final static int[][] BASS_STRIP_ORDERING = { - {0, FORWARD }, - {1, FORWARD }, - {2, FORWARD }, - {3, FORWARD }, - {4, FORWARD }, - {5, FORWARD }, - {6, FORWARD }, - {7, FORWARD }, - {8, FORWARD }, - {9, FORWARD }, - {10, FORWARD }, - {11, FORWARD }, - {12, FORWARD }, - {13, FORWARD }, - {14, FORWARD }, - {15, FORWARD }, - {16, FORWARD }, - {17, FORWARD }, - {18, FORWARD }, - {19, FORWARD }, - {20, FORWARD }, - {21, FORWARD }, - {22, FORWARD }, + // front face, counterclockwise from bottom front left + {2, BACKWARD }, + {1, BACKWARD }, + {0, BACKWARD }, + {3, BACKWARD }, + + // left face, counterclockwise from bottom front left + {13, BACKWARD }, + {12, BACKWARD }, + {15, BACKWARD }, + {14, BACKWARD }, + + // back face, counterclockwise from bottom rear left + {9, BACKWARD }, + {8, BACKWARD }, + {11, BACKWARD }, + {10, BACKWARD }, + + // right face, counterclockwise from bottom rear right + {5, BACKWARD }, + {4, BACKWARD }, + {7, BACKWARD }, + {6, BACKWARD }, + }; + + private final static int[][] STRUT_STRIP_ORDERING = { + {6, BACKWARD}, + {5, FORWARD}, + {4, BACKWARD}, + {3, FORWARD}, + {2, BACKWARD}, + {1, FORWARD}, + {0, BACKWARD, /* dummy pixels at the end of string, remove when strip is fixed */ 4}, + {7, FORWARD}, }; private final static int[][] FLOOR_STRIP_ORDERING = { {0, FORWARD}, {1, FORWARD}, {2, FORWARD}, - {3, FORWARD}, + {3, BACKWARD}, }; - private final static int[][] SPEAKER_STRIP_ORDERING = { - {0, FORWARD }, - {1, FORWARD }, - {2, FORWARD }, - {3, FORWARD }, - {4, FORWARD }, - {5, FORWARD }, - {6, FORWARD }, - {7, FORWARD }, - {8, FORWARD }, - {9, FORWARD }, - {10, FORWARD }, - {11, FORWARD }, - {12, FORWARD }, - {13, FORWARD }, - {14, FORWARD }, - {15, FORWARD }, + // The speakers are currently configured to be wired the same + // as cubes with Wiring.FRONT_LEFT. If this needs to be changed, + // remove this null assignment and change the below to have mappings + // for the LEFT and RIGHT speaker + private final static int[][][] SPEAKER_STRIP_ORDERING = null; /* { + // Left speaker + { + // Front face, counter-clockwise from bottom left + {2, BACKWARD }, + {1, BACKWARD }, + {0, BACKWARD }, + {3, BACKWARD }, + }, + // Right speaker + { + // Front face, counter-clockwise from bottom left + {2, BACKWARD }, + {1, BACKWARD }, + {0, BACKWARD }, + {3, BACKWARD }, + } + };*/ + + private final static int[][] LEFT_SPEAKER_STRIP_ORDERING = { }; public PandaDriver(String ip, Model model, PandaMapping pm) { @@ -163,19 +181,37 @@ public static class PandaDriver { case ChannelMapping.MODE_BASS: for (int[] config : BASS_STRIP_ORDERING) { pi = mapStrip(model.bassBox.strips.get(config[0]), config[1], points, pi); + if (config.length >= 3) pi += config[2]; } break; - case ChannelMapping.MODE_FLOOR: + case ChannelMapping.MODE_STRUTS_AND_FLOOR: + for (int[] config : STRUT_STRIP_ORDERING) { + pi = mapStrip(model.bassBox.struts.get(config[0]), config[1], points, pi); + if (config.length >= 3) pi += config[2]; + } for (int[] config : FLOOR_STRIP_ORDERING) { pi = mapStrip(model.boothFloor.strips.get(config[0]), config[1], points, pi); + if (config.length >= 3) pi += config[2]; } break; case ChannelMapping.MODE_SPEAKER: - for (int[] config : SPEAKER_STRIP_ORDERING) { + int [][] speakerStripOrdering; + if (SPEAKER_STRIP_ORDERING == null) { + // Copy the cube strip ordering + int[] frontLeftCubeWiring = CUBE_STRIP_ORDERINGS[0]; + speakerStripOrdering = new int[frontLeftCubeWiring.length][]; + for (int i = 0; i < frontLeftCubeWiring.length; ++i) { + speakerStripOrdering[i] = new int[] { frontLeftCubeWiring[0], BACKWARD }; + } + } else { + speakerStripOrdering = SPEAKER_STRIP_ORDERING[channel.objectIndices[0]]; + } + for (int[] config : speakerStripOrdering) { Speaker speaker = model.speakers.get(channel.objectIndices[0]); pi = mapStrip(speaker.strips.get(config[0]), config[1], points, pi); + if (config.length >= 3) pi += config[2]; } break; @@ -205,6 +241,20 @@ public static class PandaDriver { return pi; } + public void disable() { + if (enabled) { + enabled = false; + println("PandaBoard/" + ip + ": OFF"); + } + } + + public void enable() { + if (!enabled) { + enabled = true; + println("PandaBoard/" + ip + ": ON"); + } + } + public void toggle() { enabled = !enabled; println("PandaBoard/" + ip + ": " + (enabled ? "ON" : "OFF")); diff --git a/code/GLucose.jar b/code/GLucose.jar index cda18e5c67486c4c0f96d62fb127e114413d602d..88f7e08aa4c0742214c5d1208f0b3efb913115e4 100644 GIT binary patch delta 2758 zcmZWrc{r4N8=e{KNRwqoVZ;$*IU*#CC2Q7X-x<3cTQSbqGI9`N&}&K7WUZ7fOJqwl zwvc__QYcGEmiXqKbA8vj&iB37`@Z*k-@p6$JU-g^Gc275Y169q@vR{w;C=T((R=Ba{(TJD}pA z`U5IKVO2t~QF;ni!mDXOTEi?#3*rLH0Tjd;wjWd+LBe1lpWx7iB7Cb+>X)%r+0G0Q zhzpDUZB$%dL9AeRoaP%xc{u6u z`dFHGeVmIxeL$Oe3!U{J!k#+g)(k8uWjbHwNYgj%)|wUD7hh~Jlyp*Pb9$juFYX)IXBy{5Lhq)^K9P>@)521bZXL8KS^X9hwzFZCGN%$F&@ZMkS zI6u1`_u#FRgOaZCvE}5XUeCZ<$ce(adcxVxqe^rdM1IFNoQ4>6y16Uj-t>@v#sCsVaZBf z-yVrvD!0^OyqffOTX9>RPC~DoaE%l|%8J*dZxGNng1_R)z>E79YzxH?d8tLH3*!Si zNCBtQhA(9QdcGcu*x5yp+8G!~?BC7=vc5c<4cs_nx&WkRMz{Y5xx@r!Y?gA{ZEyIv;q_*ZpKm~f^{ z-zJy9Wgk0mzAo4ARJFR%#~Il;R{#hs(n zyiBZ67Prja2}jqy*L){1wa>vFuio_333W#_j5K=A-el`k;|qR7#R2Z=Pn6KUXLR#C zk?kn0aPen$9NEb#CR21Ix=!FV^2q)j@1_==Z^=*g(ahavzuTb)NwOtV)IWMQ?==iZyI(sxiO#EtuEw}2hDr@wT!PR zw}xQYVq=t~r86@h-0I&m?|dF|ukXCm85ts3Hsv>GW#p@Z0**2*@;!Q-k zc-xAXy-oApah1D|K90C!rAm<=f-8=yP3|94aDMz`S@i)|z_)7u%JKPwW{zf~v0zxp z%05Toy(oH~&VD+$q#7m?-dxL6%N|z8q{T?EnCECyGONqDC2=5Hm%DlRbQ9u;oortV zU5UA^9WSn2T(Adyyr%b^lM|CoOd?Rv%(OuN=$P!C1DU=ks#y)Pa zD48!3ycL*}^F3iF=FV6%jNI(Yi8(oTefX=lJ1KS40p9hj98snMx9*C6)g$a$zrT*_^!o#fx2Pws9ls^o4iN3d~M|DJbYx$JR8}F$r@Tq z&qD=dzpg5|KivbLj?UA2Ibf$b-8OOcm34BWNo@)ba#O;;g&forUTi)fw>0x<$Rdxy zPO$v`-AVN3>zP@ho<<^5U8&0Xdddpp3Qyg^_qX{iu~(dci8Q?*+TxAE`(Jb)uDNHR1&vLK}92i7Dm zl*KI!Dw!Eg2?V(|nTt*ZTj6zUgSHS7KZasQf(G!MhxJ zUn{El?{tK@FyL z`+NCu#$O-X)usmrX4M-v^9=cFmCmLyeJ;V<4-e~prre&T+pkjl&Wj2ICjdz<0c>rlFl`iMCx#D0M0rF*|#n_W7yA8_A+^mCW175%1V zqe$+$K#Y1gSDn&OIwaPUIZ&RTzgg0ZI*xbi z`Dy?5{X*vZM(@+t%QsvQjpW$DN4#P$_OXwA`UTDY>fF-UdXT?nz)Hj)v?t#r{DBt` zpKlRWQ+L9AML)?9ooB*|Kd#|Vl-roR_QIaUKoBjoT9lNqGwaE6)ZE>+cmnzFK)?tA z`*D<5RSK^PwRewPP@(qfbC?4Y_h&y-$)CZ246ivQMabihMZL$8nXP z%CRbZRG+QJEVvJ#r1ciG!?eSwa)f>%byN^B45y}fW~@vNTQm8cy3IK&1t?`M0nDuB z!RRDw6_5>DOM+7eoy}zs6l@eh2(nQCEj2dSzeO~Fu;l}lOSa0Oa?AEA$SQ1ALD;c1 z0zu2}1_%%BOhEW%X9|M4y#)wa_B#JNoow4vb6moqVKux!9xor@gJTB0p*S@32*|*R zgM>^W%D^&C2qb45&`>NO<^T>zfE|SpfnWzAUhtT4Og$_Tg$w}C qoW;PEac4A))f6~15dgSds0S7fR76ceULS{?|%UE@ZypH delta 2762 zcmZXWc|4SB8^>qH5@T=-lV!%5Y-0>2d$tVI*h0xNGsf5z2}ufLC*FuBMRsvimO_W2 zvd2;B6lKZI`_5@QLS-B3ooCMH{q#AX_xa=g-q-KBuiyRL_w&bfwGBhlhM@=-2R?o{ z1R?~1yx_}6Xu?ML@{RIf-1+ay;P_FMI|33=Z4j1FEQm0u*59HW&;mt*(?O^Xhz%&7 zi&k~mgdmqK!VmbsunnQb4|3_39Vo~-D(_CS5NlM7Xi)4p>3G9mo!m~o^^{Z|%FDWzbrB#g8mN{RfCwv-z< zb@J{}yMk27wlx7a_*xi^j}pcvDe+X}Z}r|<D{NcfC;P?p-a}XiLtn zATf1YuL2-Sx3=bG1d}-hz8Zj$?Un)aBuvsC_0bs|g)8+SQ_# zSIyXQ>`Ztzl1h73-qbAF9I{))e}{4;A=nVW7Zvwq6_-2N-ME7v?<+cXS1Eyfg{*{Nm3HuVln|R}<}Z zb~=}Anw>{Ib_&v|TKP9B2la~snh4?SFXR;Qugl2h?5|8YF8QhY{M?YH8+z`y#h zxoDn=y%aq0YrFaMzdA>VEl&rr>rt^u>%Ns6{&5r6ntr0Q)~FDFwKLk-piqw~ z(rThLnH;`!z(7Ub!rpG=CZ4W*tB5E9sqGnixQPujNgQxwOhs-0>V z3{3Ioh^of9s-uC@AptT_^#@FTZW;3rK0`hAe?ttk5$t9nW6x(4w|%3aSv1JtqQ ze~We8E=uFf2hk&cN1-NO-Sb05=u(fbIXt@KCp08G=dy-%UY|fP`wwp69ER4+n5bbl zudEX7HyJF=uNqsAvn?0$;NNSO{@BXNb}c=jH>f!mySexD)m%Yk#G7}O%dhfl)lOG; zg%SvF^mP+Wiz^G?*PRNt7hY*JyDZmcB7s$n4pJ_;Me|$;su?*_(C%&5d=#l7oZEzSo!4)=OGG4sOdGvoC^oX2Mi9%DPglC_?62-;SuY@Us3 zM+!%b7k@&`_Ki(ZKg1gz*ByJEd`_R>UNo0=Nb8hjl zq|0Qi*r|?;waweNZeCKg>jr3cN!jb5Q>RBxZ(UmY;k|p3@kMB3RI)KgK^!Xc&Q4qv zb0EOlY|#^S=HPCZ)(m-T&wUHBIHxs3WQp&gFC?w{qP{~IrsD(X`1!0gvfjn*tb#es zs!i*@WA2OM5(?896-|9=j83LXdId4AR3mSA-PlW^ZXiT?I0*Ojpmef#q#d=+WQ$Yyt$OX;rT3rd5skutdnA{^~bA_%uqBUU&riO^AbfP5OZFif!sOJoTC>n~P#)HZlt9 z6zD0zD7nABUA&cu@)4G?(j zLBs`DuTmc9Bn_UP{KFi%*OK|MICkr1NMjdY<_uLm&!;=9W|4W;h$TIlol5Ps?q|@f zuV#nelPb}oE2f17e7L|7cps&|VYdURZC zF0rEl*>eE;fPYm+Gws4k86LMxOSxne$>@EY$+A(l>B`;oAqB{0+GNmlNk=lAbdRMW zY;{TaFPsDA%FNNMeG~6752b2w`*TtVaQ(uF>vK={!bh2v_VVzc6<t7G%d7E8{b-}CYkK!<%b@fLcw(m2YYF}iiw=Xv8f14ST zVhKjw9J39x6bcEn=i)wNay=}R6#;13x>`h#H!W`b4RymL*ei#W3=$v_T?(Gwg z$g`s1xBPykIlOc%JS!mY;o7bI`L#l$vIkzI4!J~3IG1Yhl3vMJomWKX{Sf$HaTnqi z_tri|BCie$sao;suD#lh09b$Rv~76xI0qLEzHiZ21gm$evhgjRJZO@{TXxh^g%^ie zDe-J_iSwWh5JBn(mmfNs$dey9*7B-8#U+Wi%dDFPFHQagmSV*8uvw zt-y$%q6fl=atK5?d_2*q@4>zmA4M?I&0qg$KppQrZ1~z}3tDpdj05qsjxC+7x T=;106UPz!a;1@xXH)!`?