Friday, July 27, 2012

Tutorial 9 - Dependencies & Libraries


Webcomic Courtesy of Ethanol & Entropy


9.1 What is a Dependency and why do I want one?


The latest version (v1.4.3) of Codea introduced the concept of Dependencies. This feature is used to address the problem that over time there are certain functions or classes that you use again and again but you don't want to have to re-type these in each project. 

Prior to version 1.4.3 the answer was to copy and paste the relevant bits from one project to another. This works ok but it is a bit slow, breaks up your workflow and can lead to version problems if you forget where the master copy is located.

A better solution is to have a library which contains all of these functions and classes which you can then "include" in your projects as required. If you select the library as a dependency to your project then all of the code (outside the Main tab) within the library becomes accessible to that project.

You don't have to create a specific Library to use dependencies, any project can be selected as a dependancy, but to address the version control issue it is better to keep this code in a consistent location. We haven't worked out if it is better to have many small libraries or one big one. Our guess is that it won't make a difference as the compiler will just lift out the code it needs and a large library won't create unnecessarily bloated byte-code. 

9.2 How do you create a Library?


A Library is just another project. There are no rules or guidelines as to what a Library should look like so we are just going to do what seems sensible and allow it to evolve over time. To provide something to talk about we created the (Reefwing Software) RSLibrary which is a collection of code which we have found useful but isn't currently available within the Codea framework. 

We have structured the Library with the Main tab containing metadata (contents, version, date, etc.) and which if run, will demonstrate the main functionality of the code in the Library. Each subsequent tab contains the code we want to share grouped in what we think is a logical fashion.

Over time we suspect that people will tailor there own libraries using the best of breed from other shared code. Feel free to adopt some or any of the code in RSLibrary. We have freely borrowed from other contributors on the Codea Forum such as Two Lives Left, Vega and Bri_G.

9.3 Download RSLibrary

  1. Main.lua v1.1 - metadata and demonstration of the library functionality.
  2. Math.lua v1.0 - math.hypot(x, y), math.hypot2(x,y), math.polar(x,y,originX,originY), math.constrain(value,min,max), math.pi(), and math.round(value).
  3. Collisions.lua v1.0 - pointInCircle(pointX, pointY, centreX, centreY, radius), pointInRect(pointX, pointY, x, y, w, h).
  4. Colors v1.0 - Definitions of the 17 predefined colours from UIColor.
  5. Button v1.2 - Modified Vega mesh Button Class.
  6. RoundRect v1.0 - Modified Bri_G Rounded Rectangle Class.
  7. TextBox v1.0 - Two Lives Left TextBox Class (from Spritely example project).

9.4 How do I link a Library to my Project?


So you have created your shiny new Library, how do you make this a dependency of your project. The process is very simple.
  1. With your Project open there is a [+] tab in the top right corner.
  2. Tapping this will pop up a menu with three options:
    • Create New Class;
    • Create Blank File; and
    • Dependencies.
  3. Not surprisingly the section called Dependencies is where you select your library. Tap it to select and it will indicate the available tabs (6 for RSLibrary).
And that's it. You can remove a dependency using the same process.

9.5 What's Next?


As the idea of Libraries and Dependencies is evolving we would welcome thoughts and observations on the best structure or alternate approaches, so that we can share them here.

9.6 The Main Class

Here is a copy of the Main Class so you can see how RSLibrary is structured.
--# Main
-- RSLibrary by Reefwing Software (www.reefwing.com.au)
-- This Library contains functions and definitions which are commonly used
-- but not currently available in the Codea framework.
-- It includes code from: Two Lives Left
--                                        Vega
--                                        Bri_G

--
-- CONTENTS
--
-- Math Extensions:

--     1. math.round(value)
--     2. math.hypot(x, y)
--     3. math.hypot2(x, y)
--     4. math.polar(x, y) or math.polar(x, y, originX, originY)
--     5. math.pi()
--     6. math.constrain(value, min, max)
--
-- Collision Detection Functions:
--     1. pointInCircle(pointX, pointY, centreX, centreY, radius)
--     2. pointInRect(pointX, pointY, x, y, width, height)
--
-- Color Extensions: UIColor Predefined Colors
--     1. blackColor        10. yellowColor
--     2. darkGrayColor 11. magentaColor
--     3. lightGrayColor 12. orangeColor
--     4. whiteColor        13. purpleColor
--     5. grayColor          14. brownColor
--     6. redColor            15. clearColor
--     7. greenColor       16. lightTextColor
--     8. blueColor          17. darkTextColor
--     9. cyanColor
--
-- Classes:

--     1. Vega Modified Mesh Button Class v1.2
--     2. Two Lives Left TextBox Class v1.0
--     3. Bri_G Modified RoundRect Class v1.0
--
-- Note the Main class of the Library is just used to demonstrate the Library
-- functionality. Nothing defined here is available using dependancies.


function setup()
    -- Library Metadata
    version = 1.1
    saveProjectInfo("Description", "Reefwing Software Library v"..version)
    saveProjectInfo("Author", "Reefwing Software")
    saveProjectInfo("Date", "26th July 2012")
    saveProjectInfo("Version", version)
    print("RSLibrary v"..version.."\n")

    -- Parameters to demonstrate the RoundRect Class
    parameter("cr",0,2,1)
    iparameter("border",0,4,2)
    iparameter("x",0,WIDTH - 200,275)
    iparameter("y",0,HEIGHT - 40,350)
    iparameter("w",100,WIDTH,200)
    iparameter("h",0,HEIGHT,40)

    -- Construct the rounded rectangle
    roundRect = RoundRect(x, y, w, h, border, "test", cr)
    -- Construct a TextBox
    textBox = TextBox(WIDTH / 2 - 100, HEIGHT - 120, 200, "Enter your name!")
    showTextBox = false
end

function draw()

    -- This sets the background color to black
    background(blackColor)
    -- Update our roundRect with the latest parameter values (not required if they
    -- don't change). The larger the width and height the smaller cr needs to be
    -- otherwise you will end up with an ellipse.

    roundRect.x = x
    roundRect.y = y
    roundRect.w = w
    roundRect.h = h
    roundRect.cr = cr
    roundRect.border = border

    -- Set the rectangle fill and border colours. Text colour is also available.

    roundRect.fillColor = blueColor
    roundRect.borderColor = redColor

    -- call the roundRect draw() function
    roundRect:draw()
    -- If tap is not on the rectangle then draw the textBox
    if showTextBox then
         textBox:draw()
    end
end

function touched(touch)

-- Use this method to demonstrate the pointInRect() collision detection function
    if touch.state == BEGAN and pointInRect(touch.x, touch.y, x, y, w, h) then
         showTextBox = false
         print("Rounded Rectangle Tapped.")
    elseif touch.state == ENDED and not pointInRect(touch.x, touch.y, x, y, w, h) then
         showTextBox = true
         showKeyboard()
    end
end


-- KeyBoard handling function
-- Used to enter name if touch is not on the rounded rectangle


function keyboard(key)
    if key ~= nil then
        if string.byte(key) == 10 then
-- <RETURN> Key pressed
            print("Hello "..textBox.text)
            showTextBox = false
            hideKeyboard()
        elseif string.byte(key) ~= 44 then
-- e.g. filter out commas
            textBox:acceptKey(key)
        end
    end
end

No comments:

Post a Comment