From b546ea74dde16a0d58ad2c2e2e8d7efb4b2a15e0 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Fri, 27 Nov 2009 03:34:50 +0000 Subject: [PATCH] updated documentation - added handler / callback prototype section, added key names section; added ag::drawArc() and ag::drawCircle() w/ documentation; added tests/transparent.png and tests/imagetest.lua; renamed init() event to init_event(); re-added missing drawImage Lua hook; changed images to draw transparent parts correctly through to background git-svn-id: svn://anubis/anaglym/trunk@197 99a6e188-d820-4881-8870-2d33a10e2619 --- .todo | 1 - Engine.cc | 80 +++++---- Engine.h | 2 + ag.cc | 63 ++++++- ag.h | 2 + doc/index.html | 365 +++++++++++++++++++++++++++++++++++--- lib/std.lua | 20 +++ tests/ballstairs.lua | 2 +- tests/bowling.lua | 2 +- tests/cannon.lua | 2 +- tests/cratestack.lua | 2 +- tests/imagetest.lua | 17 ++ tests/managed_objects.lua | 2 +- tests/transparent.png | Bin 0 -> 2250 bytes 14 files changed, 493 insertions(+), 67 deletions(-) create mode 100644 tests/imagetest.lua create mode 100644 tests/transparent.png diff --git a/.todo b/.todo index 271309f..3880b85 100644 --- a/.todo +++ b/.todo @@ -1,2 +1 @@ - finish bowling demo -- finish ag and std documentation diff --git a/Engine.cc b/Engine.cc index 53a1443..387bdc7 100644 --- a/Engine.cc +++ b/Engine.cc @@ -215,7 +215,7 @@ bool Engine::load(const char * program) void Engine::checkForAllHandlerFunctions() { - checkForFunction("init", init); + checkForFunction("init_event", init); checkForFunction("update_event", update); checkForFunction("update_overlay_event", update_overlay); checkForFunction("key_down_event", key_down); @@ -557,8 +557,6 @@ void Engine::drawLine(float r, float g, float b, { checkGLError(); glPushAttrib(GL_LINE_BIT | GL_ENABLE_BIT); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); glEnable(GL_LINE_SMOOTH); glLineWidth(width); glBegin(GL_LINES); @@ -578,8 +576,6 @@ void Engine::drawRect(float r, float g, float b, glPushMatrix(); glTranslatef(x, y, 0); glRotatef(rot, 0, 0, 1); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); glEnable(GL_LINE_SMOOTH); glBegin(GL_LINE_LOOP); glColor3f(r, g, b); @@ -601,8 +597,6 @@ void Engine::fillRect(float r, float g, float b, glPushMatrix(); glTranslatef(x, y, 0); glRotatef(rot, 0, 0, 1); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); glEnable(GL_POLYGON_SMOOTH); glBegin(GL_QUADS); glColor3f(r, g, b); @@ -620,14 +614,16 @@ void Engine::drawImage(float width, float height, float x, float y, int tex, float rot) { checkGLError(); - glPushAttrib(GL_ENABLE_BIT); + /* TODO: check following flags */ + glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_LIGHTING_BIT); glPushMatrix(); glTranslatef(x, y, 0); glRotatef(rot, 0, 0, 1); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); glEnable(GL_POLYGON_SMOOTH); glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBindTexture(GL_TEXTURE_2D, tex); glBegin(GL_QUADS); glTexCoord2f(0, 0); @@ -644,6 +640,29 @@ void Engine::drawImage(float width, float height, float x, float y, checkGLError(); } +void Engine::drawArc(float r, float g, float b, float x, float y, + float radius, float a1, float a2) +{ + a1 *= M_PI / 180.0f; + a2 *= M_PI / 180.0f; + int segments = 1 + (int) (fabsf(a2 - a1) / (M_2_PI / 16.0f)); + float step = (a2 - a1) / segments; + glPushAttrib(GL_ENABLE_BIT); + glPushMatrix(); + glTranslatef(x, y, 0); + glColor3f(r, g, b); + glBegin(GL_LINE_STRIP); + float angle = a1; + for (int i = 0; i <= segments; i++) + { + glVertex2f(radius * cos(angle), radius * sin(angle)); + angle += step; + } + glEnd(); + glPopMatrix(); + glPopAttrib(); +} + /* called by SDL when the update timer expires */ Uint32 Engine::updateCallback(Uint32 interval, void * param) { @@ -762,27 +781,28 @@ void Engine::update_overlay_event() { checkGLError(); glPushAttrib(GL_ENABLE_BIT); - glDisable(GL_DEPTH_TEST); - int width = m_video.getWidth(); - int height = m_video.getHeight(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, width, 0, height, -1.01, 1.01); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + int width = m_video.getWidth(); + int height = m_video.getHeight(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, width, 0, height, -1.01, 1.01); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); - lua_getfield(m_luaState, LUA_GLOBALSINDEX, - EVENT_HANDLER_AG_NAME(update_overlay)); - lua_pushnumber(m_luaState, width); - lua_pushnumber(m_luaState, height); - /* call the update_overlay function - * - pops the function ref from the stack, then the arguments */ - int s = lua_pcall(m_luaState, 2, 0, 0); - reportErrors(s); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); + lua_getfield(m_luaState, LUA_GLOBALSINDEX, + EVENT_HANDLER_AG_NAME(update_overlay)); + lua_pushnumber(m_luaState, width); + lua_pushnumber(m_luaState, height); + /* call the update_overlay function + * - pops the function ref from the stack, then the arguments */ + int s = lua_pcall(m_luaState, 2, 0, 0); + reportErrors(s); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); glPopAttrib(); checkGLError(); } diff --git a/Engine.h b/Engine.h index 6eee940..1a2c841 100644 --- a/Engine.h +++ b/Engine.h @@ -151,6 +151,8 @@ class Engine float width, float height, float x, float y, float rot = 0.0f); void drawImage(float width, float height, float x, float y, int tex, float rot = 0.0f); + void drawArc(float r, float g, float b, float x, float y, + float radius, float a1, float a2); /* lua services */ int setCamera(lua_State * L); diff --git a/ag.cc b/ag.cc index 08f93b0..fdc43ec 100644 --- a/ag.cc +++ b/ag.cc @@ -23,6 +23,9 @@ namespace ag static const luaL_Reg functions[] = { { "clearEventHandler", clearEventHandler }, { "doPhysics", doPhysics }, + { "drawArc", drawArc }, + { "drawCircle", drawCircle }, + { "drawImage", drawImage }, { "drawLine", drawLine }, { "drawObjects", drawObjects }, { "drawRect", drawRect }, @@ -55,8 +58,8 @@ namespace ag { "createBoxStatic", createBoxStatic }, { "createCapsule", createCapsule }, { "createCapsuleStatic", createCapsuleStatic }, - { "createCylinder", createCylinder }, - { "createCylinderStatic", createCylinderStatic }, +// { "createCylinder", createCylinder }, +// { "createCylinderStatic", createCylinderStatic }, { "createPlane", createPlane }, { "createSphere", createSphere }, { "createSphereStatic", createSphereStatic }, @@ -478,13 +481,67 @@ namespace ag return 0; } + int drawArc(lua_State * L) + { + int argc = lua_gettop(L); + if (argc == 8) + { + bool valid = true; + for (int i = 1; i <= argc; i++) + { + if (!lua_isnumber(L, i)) + valid = false; + } + if (valid) + { + g_engine->drawArc( + lua_tonumber(L, 1), + lua_tonumber(L, 2), + lua_tonumber(L, 3), + lua_tonumber(L, 4), + lua_tonumber(L, 5), + lua_tonumber(L, 6), + lua_tonumber(L, 7), + lua_tonumber(L, 8)); + } + } + return 0; + } + + int drawCircle(lua_State * L) + { + int argc = lua_gettop(L); + if (argc == 6) + { + bool valid = true; + for (int i = 1; i <= argc; i++) + { + if (!lua_isnumber(L, i)) + valid = false; + } + if (valid) + { + g_engine->drawArc( + lua_tonumber(L, 1), + lua_tonumber(L, 2), + lua_tonumber(L, 3), + lua_tonumber(L, 4), + lua_tonumber(L, 5), + lua_tonumber(L, 6), + 0.0, + 360.0); + } + } + return 0; + } + int drawImage(lua_State * L) { int argc = lua_gettop(L); if (argc == 5 || argc == 6) { bool valid = true; - for (int i = 1; i <= 6; i++) + for (int i = 1; i <= argc; i++) if (i != 5 && !lua_isnumber(L, i)) valid = false; if (!lua_istable(L, 5)) diff --git a/ag.h b/ag.h index 8d54356..4ae18c4 100644 --- a/ag.h +++ b/ag.h @@ -34,6 +34,8 @@ namespace ag int startFrame(lua_State * L); /* 2D overlay functions */ + int drawArc(lua_State * L); + int drawCircle(lua_State * L); int drawImage(lua_State * L); int drawLine(lua_State * L); int drawRect(lua_State * L); diff --git a/doc/index.html b/doc/index.html index 1eb7394..7853454 100644 --- a/doc/index.html +++ b/doc/index.html @@ -29,6 +29,8 @@ The library functions are documented below.
  • std library
  • +
  • Lua Event Handlers
  • +
  • Key Names

  • @@ -42,16 +44,8 @@ The library functions are documented below.

    This function removes a Lua callback function from being associated with the engine event specified. -event_name should be one of the following strings: -

    +event_name should be a string containing an event name. +See Lua event handlers for a list of events.

    @@ -64,6 +58,48 @@ mode is enabled; this mode is enabled by default. See setAutoPhysics for more information.

    + +

    drawArc

    +

    ag.drawArc(r, g, b, x, y, radius, a1, a2)

    +

    +This function tells the engine to draw an arc to the screen. +The color of the arc is specified by (r, g, b). +x and y specify the coordinates of the center +of the arc. +The arc ranges from angle a1 to a2, which are +specified in degrees, going counter-clockwise from the East. +drawArc() should normally be called from the +update_overlay event handler. +

    + +
    +

    drawCircle

    +

    ag.drawCircle(r, g, b, x, y, radius)

    +

    +This function tells the engine to draw a circle to the screen. +The color of the circle is specified by (r, g, b). +x and y specify the coordinates of the center +of the circle. +drawCircle() should normally be called from the +update_overlay event handler. +

    + +
    +

    drawImage

    +

    ag.drawImage(width, height, x, y, texture, [, rot])

    +

    +This function tells the engine to draw an image to the screen. +width and height specify the size of the image, +in pixels. +x and y specify the coordinates of the center +of the image. +The image to be drawn is specified by texture, which +should be loaded by +ag.loadTexture(). +drawImage() should normally be called from the +update_overlay event handler. +

    +

    drawLine

    ag.drawLine(r, g, b, x1, y1, x2, y2 [, width])

    @@ -206,7 +242,7 @@ libraries such as
    std. This function returns a boolean value for whether or not the key given by key is currently pressed or not. key should be a string corresponding to a key name. -See keys for key names. +See Key Names for key names.

    @@ -280,16 +316,8 @@ occurs. There can be only one registered handler per event, so if a previous Lua function was registered for event_name, it will be overwritten to call the new handler. -event_name should be one of: - +event_name should be a string containing the name of the event. +See Lua event handlers for a list of event names.

    When the Lua script is initially loaded, any functions found @@ -624,6 +652,16 @@ ag.import("std")

    + +

    createPlanePointNormal

    +

    obj = std.createPlanePointNormal(x, y, z, nx, ny, nz)

    +

    +This function simply wraps +ag.createPlane() +allowing the user to specify a point on the plane with coordinates (x, y, z) +and the surface normal of the plane (nx, ny, nz). +

    +

    crossProduct

    cp = std.crossProduct(vec1, vec2)

    @@ -648,15 +686,286 @@ The indices of vec1 and vec2 should be from 1 to 3. This allows specifying a Lua array directly as a parameter to the function.

    -
    -

    createPlanePointNormal

    -

    obj = std.createPlanePointNormal(x, y, z, nx, ny, nz)

    +
    +

    pctx

    +

    pixels_x = std.pctx(percent)

    -This function simply wraps -ag.createPlane() -allowing the user to specify a point on the plane with coordinates (x, y, z) -and the surface normal of the plane (nx, ny, nz). +This function returns the number of pixels corresponding to +percent percent of the screen's X axis. +percent should be between 0.0 and 1.0.

    + +

    pcty

    +

    pixels_y = std.pcty(percent)

    +

    +This function returns the number of pixels corresponding to +percent percent of the screen's Y axis. +percent should be between 0.0 and 1.0. +

    + + +
    + +
    +

    Lua Event Handlers

    + +

    +The following is a list of all events that the Anaglym engine +handles: +

    +Lua callback functions may be registered for each of these events. +

    + +

    +When a Lua script is loaded, any defined function that matches the +name of an engine event with "_event" appended will +automatically be registered as a callback for that engine event. +For example, a function named key_up_event() in +a loaded Lua script will automatically be registered to receive +key_up event notifications. +

    + +

    +The prototypes for the Lua event handler callback functions follow. +

    + + +

    update

    +

    function update_event()

    +

    +This function is called for every frame that is drawn on the screen. +It could be called up to a few dozen times per second. +

    + +
    +

    update_overlay

    +

    function update_overlay_event(width, height)

    +

    +This function is called immediately after the update +function. +During this event, the coordinate system is reset from the normal +3D system to a 2D system. +The coordinates range from (0, 0) in the lower-left corner of the +screen to (width, height) in the upper-right corner of the screen. +In this mode, depth testing is disabled, so any graphics drawn will +"overlay" whatever is currently on the screen. +width and height contain the size of the +Anaglym engine window, in pixels. +

    + +
    +

    key_down

    +

    function key_down_event(key_name)

    +

    +This function is called when a key is initially pressed. +key_name is a string containing the name of the +key pressed. +See Key Names for a list of key names. +

    + + +

    key_up

    +

    function key_up_event(key_name)

    +

    +This function is called when a key is released. +key_name is a string containing the name of the +key released. +See Key Names for a list of key names. +

    + + +

    mousebutton_down

    +

    function mousebutton_down_event(button, x, y)

    +

    +This function is called when a mouse button is pressed. +button contains the number of the button pressed +(1: left, 2: middle, 3: right, 4: scroll wheel up, 5: scroll wheel down). +The position of the cursor when the mouse button was pressed is at +(x, y). +This position is only meaningful when the cursor is shown. +By default, it is hidden. +Cursor visibility can be toggled with the F2 key. +

    + +
    +

    mousebutton_up

    +

    function mousebutton_up_event(button, x, y)

    +

    +This function is called when a mouse button is released. +button contains the number of the button released +(1: left, 2: middle, 3: right, 4: scroll wheel up, 5: scroll wheel down). +The position of the cursor when the mouse button was released is at +(x, y). +This position is only meaningful when the cursor is shown. +By default, it is hidden. +Cursor visibility can be toggled with the F2 key. +

    + +
    +

    mouse_motion

    +

    function mouse_motion_event(x, y, xrel, yrel)

    +

    +This function is called when the mouse moves. +(x, y) specifies the position that the cursor was moved to. +This position is only meaningful when the cursor is shown. +xrel and yrel contain the relative +movement distance. +These values are meaningful regardless of the cursor visibility state. +

    + + +
    + +
    +

    Key Names

    + + + diff --git a/lib/std.lua b/lib/std.lua index 8626d73..2554cf9 100644 --- a/lib/std.lua +++ b/lib/std.lua @@ -43,3 +43,23 @@ std.createPlanePointNormal = function(x, y, z, nx, ny, nz) -- invoke the ag routine to create a plane based on a, b, c, d parameters return ag.createPlane(nx, ny, nz, nx * x, ny * y, nz * z) end + +-- Convert screen percent to pixel count along X axis +-- Input: +-- percent: percent (0.0 - 1.0) of the screen's X axis +-- Output: +-- Number of pixels corresponding to 'percent' percent of the X axis +std.pctx = function(percent) + local width = ag.getScreenSize() + return width * percent +end + +-- Convert screen percent to pixel count along Y axis +-- Input: +-- percent: percent (0.0 - 1.0) of the screen's Y axis +-- Output: +-- Number of pixels corresponding to 'percent' percent of the Y axis +std.pcty = function(percent) + local width, height = ag.getScreenSize() + return height * percent +end diff --git a/tests/ballstairs.lua b/tests/ballstairs.lua index b91bcfb..94cd6f8 100755 --- a/tests/ballstairs.lua +++ b/tests/ballstairs.lua @@ -1,5 +1,5 @@ -function init() +function init_event() arena = ag.loadModelStatic("boxarena") ball = ag.loadModel("checkerball") ball:setPosition(-7, 7.4, 12) diff --git a/tests/bowling.lua b/tests/bowling.lua index fe748c9..246ab77 100644 --- a/tests/bowling.lua +++ b/tests/bowling.lua @@ -1,5 +1,5 @@ -function init() +function init_event() local rows = 5 local offset = 1 local pin = ag.loadModel("bowling_pin", 0.5) diff --git a/tests/cannon.lua b/tests/cannon.lua index de69779..906a76c 100644 --- a/tests/cannon.lua +++ b/tests/cannon.lua @@ -1,5 +1,5 @@ -function init() +function init_event() local levels = 7 local crate = ag.loadModel("crate", 0.5) local offset = 0.5 diff --git a/tests/cratestack.lua b/tests/cratestack.lua index cbc9772..d462e90 100644 --- a/tests/cratestack.lua +++ b/tests/cratestack.lua @@ -1,5 +1,5 @@ -function init() +function init_event() crates = {} levels = 5 diff --git a/tests/imagetest.lua b/tests/imagetest.lua new file mode 100644 index 0000000..a0bd184 --- /dev/null +++ b/tests/imagetest.lua @@ -0,0 +1,17 @@ + +function init_event() + ag.import("std") + image = ag.loadTexture("transparent.png") + local crate = ag.loadModelStatic("crate") + ag.setCamera(5, -5, 5) +end + +function update_overlay_event(width, height) + local time = ag.elapsedTime() + local rot = time / 1000 * 90 + local scale = math.sin(time / 1000) * 1.5 / 2 + 1.25 + ag.drawImage(256 * scale, 256 * scale, width / 2, height / 2, image, rot); + ag.drawArc(1, 0, 0.5, width * 0.8, height * 0.8, height * 0.15, + 90, 180); + ag.drawCircle(0, 0, 1, width * 0.8, height * 0.2, height * 0.15); +end diff --git a/tests/managed_objects.lua b/tests/managed_objects.lua index 11971ae..d970933 100644 --- a/tests/managed_objects.lua +++ b/tests/managed_objects.lua @@ -1,5 +1,5 @@ -function init() +function init_event() local crate_texture = ag.loadTexture("checker.jpg") local ground = ag.createPlaneStatic(0, 0, 1, 0) ground:setColor(0.2, 1.0, 0.2) diff --git a/tests/transparent.png b/tests/transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..e811708d27305b7a1444d8030d9376014ea759e6 GIT binary patch literal 2250 zcmaJ@do&aL8~<*$*;uneuCWwWDVMF3B5aadM#-J!lH8(PhPlklo$`j}(nP&eV`285YF_ko32yc_#`04N8Iq!KcpYwdqdH#8x^PJC=_ItTo3@Y@ra~J@U(!T=&if+ko zk>cSNHfG|Jl8Umby83IF8(Wk@xS3NpDI_@9_i{KOUG=>X?(2i2`G;S^nOoRU91}5e z03g*Z$R_8aroM1I(_|f#dpqy$DGcTcF9x{MtF-LA*(^Nk77Vpl)jkdHl~4X?7mN42 z8kdGg($m!FFL0SE%u-x`xSs_<&QT3b;L_!|vWIPGc1_nxR4(qUVYkxahyEEZB+wI7 zo3&~2q7s5%$2={5ei1Nn$bWSoV_MyBxe&R$X^*k%2YY8CbSoZbQmi7Km&t9qX`{VJ(f9OBsZN=bA6FvuVvRb^ds%)0 zWjUDlMC#dLnIwE%L1@TN_s?(`D1nW*%)11}W^>)oTOSby8r5Qq4NcgfazKDOgbmt= zumc?M;c&B&cH<-2yICKOQl+0Csp#L>DF>%`)s^WaQNt=q<8FDPjy!gk;h{B@bYyw2 zm=XeH*Trj@(1Jl9QdvHgK&_2k-_cTVAt)E2*;4J_1396gRQ=B`f}?>4vi$g4jjBdx zF5shPB+qV$K;E!B@IS%ZW_`pBU09_y{S%B&{D+U_t*;dcFN=I8PVmgOy2XiJyR&u7 z*@kkw#Y53RvqjtC@Sm)78+PAG+dW&0n!{wmWn|W7?b{~oCt#th_mI2~FV=LrE(RyU zm*l!W#81GmE|rCOhsKwok^*z)5VQ#`FTLotWm zpnu$rE`EE6a|i9!(1HgU;?4adzaCmI8RzJw&9&kg`=SptrTiBz3xcr&R7d?PaK=T3 z*VWc)*;6Xz1+PX?bEt&e2f-WT_HDCzoN5$fM_WBkv8R4JUiOLRl5|(k6OB^UzJdG- zohOGZ9sRE+>Ss`j#$2--Ocj{yOi-c8H-rh?45H{*p9aYI6yUvpVwF~Hdiuto zWE14SW`ngH>M>aaf>nnU0LB0dNkfr#Z(}rSEkH;^K>f^%ryhH2;+A-XXZ;!sY`u+w zNN+AeTNm@Ozf7gZ2YGd~*6Ai1ns#yFnzYvZy@1v{4~cS`vG8PBJ)-uHHLI_Y%Q9UR z&qvUdovDM7ePY14keCKhemLpy(}UFXU{wRCibWktwwm`aA$_|_)BTfFsMyha7!u2V zt6v^PDVMXnoiZBI-C)QV)g}ocpaxeYu*mH!9P}j%?_BvzndVrbi@u3|DJ0Aw1V7fE zb;o|tQ*5AI^KQ+ll*Dnx<(YJ?!7|h(bCbe6yM}wr>_$-I&m(jHfdfaC`n#yhs`KeFC#2YJazl%>GF`1#~6Dwed?W@ zs@rkrT9zWfy4Cn&|M<^XOYhin}yph;$bS2zTDR z9)x~Oibjk?nXf86e%#B@y=Y=znK54xsi*5i zc2;Y|r;6_&76W2#b~-hOu70pw!Rd7nwE3yeN`g>)NviDU4;}BK-CY%w)bm%APhQ>A z_2EWho78RtE?yV}h7FVA(r!LJ%naeSa722(!#;&r`8X>6OAXSW?W~$sWlRxnqpXuT zB8kTx?DV^+Qe{c{UpfEaABV@9lva_rdDB^!pp}siCY8wp?^>TQT`>@AO@4vFSi<$Y zHi3RAsj7g_`1aDN2qD;4Ef^o?dUxutcJ6*eIp#NFttzZj?vr$#Ki3UU#xe>cl78H( zGdniwi;!UM+B#g>q|Gz_Y*jz&m)}3%v$L~hP4uSTG-L=TUmDc`d8o;8s7|Zj%=b?Y zaT>+n+I&xE5u8}T17d?ilVYLEO1UM%@ah3w5*0&?k~q z`RI3aZJbNA+YD*VL&`g#6+(=8uoA!Yc{NTpd){Dw>8{DZwz!i8I$ALpOPWi+m95Q` zValj5Wi%<`YlP02Qg@B5_veFO*ggBkzFe*}q^ze$WvvHrvOH2oUze^1SrhN9pkr!W zw-2^s9fZ|uChR~ApIR)nRG`)~i;hRSbOl>q1+XwXL#{TZGXDq9m+6rJ literal 0 HcmV?d00001