Wiki

Clone wiki

Core / loveCodea.lua

loveCodea.lua

Version on github

The most recent and comprehensive version of loveCodea.lua is published here on github.

The author has included some scripts in the tools folder with the aim of making the workflow very easy. More information is in the README.md on github.

With this version of loveCodea.lua, you can:

  1. run many examples like Cargo-Bot and others found on the Codea Forum;
  2. start an small file server and browse your desktop files on the iPad; and
  3. even start a small Codea project to immediately test the code with the real Codea.

Note for power users: The "all lua files" feature of the file server does not observe plists nor does it scan the class hierarchy like the "stage" tool does.

Earlier versions (31 May 2012)

The version of loveCodea.lua published on 31 May 2012, for a month is reproduced below:

--
-- Inlined module: loveCodeaBase.lua
--

--[[
Quick usage:
Copy this file into the project folder.
Add this to the top of the main.lua file:
  if require ~= nil then
    require("loveCodea")
    require(" ... extra Lua files ... ")
    require(" ... of your project ... ")
  end
Create a conf.lua file with these contents:
  function love.conf(t)
    t.screen.width = 1024
    t.screen.height = 786
  end
Run your project.
]]--

--[[
LoveCodea is an update of LoveCodify.
See https://github.com/SiENcE/lovecodify
2012 Stephan Effelsberg

Main topics of the update:
- Make the wrapper running with Love2D 0.8.0.
  Do not use it with versions < 0.8.0, they made incompatible changes.
- Make Asteroyds run. Like the original LoveCodify, work on LoveCodea wasn't
  started to get a full featured wrapper (I'd be glad if we get there, however)
  but with a specific target in mind.

Changes (too many, I stopped listing them all):
- Love 0.8.0: all times in seconds now
- Improve mirroring (TODO need to test the MIRROR variable at some places)
- Correct touch control (Love 0.8.0 calls events before update)
- Implement tint()
- Get rid of bogus _resetColor()
- Implement sprite scaling
- Implement font size
- Implement different colors used by lines, text, ...
- Implement framed rectangles and circles
- Enough meshes for Backgammon and the Hello Mesh example
- Implement setContext with a Canvas in a way that allows
  drawing and setting of pixels (full transformation unrolling
  because of Codea's lower left coordinate system origin)
- Implement resetMatrix()
]]--

--[[
LoveCodify is a Wrapper Class to run Codify/Codea Scripts with Love2D
Copyright (c) 2010 Florian^SiENcE^schattenkind.net

Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php

You can use the http://love2d.org/ runtime to code Codify Apps on MacOSX/Linux/Windows.
Beware, it's unfinished, but samples are running.

Just include the this in your Codify project:
dofile ("loveCodify.lua")
]]--

------------------------
-- loveCodify SETTINGS
------------------------
if MIRROR == nil then
    MIRROR = true
end
if LOVECODEAHUD == nil then
    LOVECODEAHUD = true
end

-- CODEA Dump https://gist.github.com/1375114
--------------------------------------------------
--[[ CODEA Constants, Variables and Functions ]]--
--------------------------------------------------

CORNER  = 0
CORNERS = 1
CENTER  = 2
RADIUS  = 3

BEGAN   = 1
MOVING  = 2
ENDED   = 4

--HEIGHT  = 748
--WIDTH   = 748

STANDARD   = 0
FULLSCREEN = 1
FULLSCREEN_NO_BUTTONS = 2

PORTRAIT_ANY         = 0
PORTRAIT_UPSIDE_DOWN = 1
LANDSCAPE_LEFT       = 2
LANDSCAPE_RIGHT      = 3
PORTRAIT_LEFT        = 4
LANDSCAPE_ANY        = 5
ANY                  = 6

ROUND      = 0
SQUARE     = 1
STATIONARY = 3
PROJECT    = 2

SOUND_BLIT = "blit"
SOUND_EXPLODE = "explode"
SOUND_HIT = "hit"
SOUND_JUMP = "jump"
SOUND_PICKUP = "pickup"
SOUND_RANDOM = "random"
SOUND_SHOOT = "shoot"

DeltaTime   = 0
ElapsedTime = 0

background = background or function() end
class = class or function() end
color = color or function() end
CurrentTouch = {} --[[ Touch
x:0.000000, y:0.000000
prevX:0.000000, prevY:0.000000
id:0
state:0
tapCount:0]]--
draw = draw or function() end
ellipse = ellipse or function() end
ellipseMode = ellipseMode or function() end
fill = fill or function() end
Gravity = {} --[[ (0.000000, 0.000000, 0.000000)]]--
iparameter = iparameter or function() end
iwatch = iwatch or function() end
line = line or function() end
lineCapMode = lineCapMode or function() end
noFill = noFill or function() end
noise = noise or function() end
noSmooth = noSmooth or function() end
noStroke = noStroke or function() end
noTint = noTint or function() end
parameter = parameter or function() end
point = point or function() end
pointSize = pointSize or function() end
popMatrix = popMatrix or function() end
popStyle = popStyle or function() end
print = print or function() end
pushMatrix = pushMatrix or function() end
pushStyle = pushStyle or function() end
rect = rect or function() end
rectMode = rectMode or function() end
resetMatrix = resetMatrix or function() end
resetStyle = resetStyle or function() end
rotate = rotate or function() end
rsqrt = rsqrt or function() end
scale = scale or function() end
setInstructionLimit = setInstructionLimit or function() end
setup = setup or function() end
smooth = smooth or function() end
sound = sound or function() end
sprite = sprite or function() end
stroke = stroke or function() end
strokeWidth = strokeWidth or function() end
tint = tint or function() end
translate = translate or function() end
UserAcceleration = {} --[[ (0.000000, 0.000000, 0.000000)]]--
vec2 = vec2 or function() end
watch = watch or function() end
zLevel = zLevel or function() end


-------------------
-- Drawing
-------------------

iparameterList = {}
parameterList = {}
iwatchList = {}
watchList = {}

CurrentTouch = {}
CurrentTouch.x = 0
CurrentTouch.y = 0
CurrentTouch.prevX = 0
CurrentTouch.prevY = 0
CurrentTouch.deltaX = 0
CurrentTouch.deltaY = 0
CurrentTouch.id = 0
CurrentTouch.state = ENDED
CurrentTouch.tapCount = 0

Gravity = {}
Gravity.x = 0
Gravity.y = 0
Gravity.z = 0
Gravity.y = -1

-- Fill Modes - line | fill
_fillmode = "line"

_rectmode = CORNER

-- LineCap Modes
_linecapmode = ROUND

-- Ellipse Modes
_ellipsemode = CENTER

-- class.lua
-- Compatible with Lua 5.1 (not 5.0).
function class(base, init)
    local c = {}    -- a new class instance
    if not init and type(base) == 'function' then
        init = base
        base = nil
    elseif type(base) == 'table' then
        -- our new class is a shallow copy of the base class!
        for i,v in pairs(base) do
            c[i] = v
        end
        c._base = base
    end
    -- the class will be the metatable for all its objects,
    -- and they will look up their methods in it.
    c.__index = c

    -- expose a constructor which can be called by <classname>(<args>)
    local mt = {}
    mt.__call = function(class_tbl, ...)
        local obj = {}
        setmetatable(obj,c)
        if class_tbl.init then
            class_tbl.init(obj,...)
        else
            -- make sure that any stuff from the base class is initialized!
            if base and base.init then
                base.init(obj, ...)
            end
        end
        return obj
    end
    c.init = init
    c.is_a = function(self, klass)
        local m = getmetatable(self)
        while m do
            if m == klass then return true end
            m = m._base
        end
        return false
    end
    setmetatable(c, mt)
    return c
end

-------------------
-- Graphics
-------------------

function color(r, g, b, a)
    local color = {}
    color.r = r
    color.g = g
    color.b = b
    color.a = a or 255
    return color
end

function background(red, green, blue, alpha)
    if (red and green and blue and alpha) then
        love.graphics.setBackgroundColor(red, green, blue, alpha)
    elseif (red and green and blue) then
        love.graphics.setBackgroundColor(red, green, blue, 255)
    elseif (type(red) == "number" and type(green) == "number") then
        -- gray, alpha
        love.graphics.setBackgroundColor(red, red, red, green)
    elseif (type(red) == "number") then
        -- gray
        love.graphics.setBackgroundColor(red, red, red, 255)
    elseif red and (red.r and red.g and red.b) then
        -- color
        love.graphics.setBackgroundColor(red.r, red.g, red.b)
    else
        love.graphics.setBackgroundColor(0, 0, 0)
    end
end

function ellipse(x, y, width, height)
    setContext(_context)
    _invalidateImageData(_context)
    if height == nil or width == height then
        local radius = width
        if _ellipsemode == CENTER then radius = width / 2 end
        if _fillmode == "fill" then
            love.graphics.setColor(unpack(_fillcolor))
            love.graphics.circle("fill", x, y, radius, 50)
        end
        local lw = _strokewidth
        love.graphics.setLineWidth(_strokewidth)
        love.graphics.setColor(unpack(_strokecolor))
        love.graphics.circle("line", x, y, radius - lw / 2, 50)
    else
        if _fillmode == "fill" then
            love.graphics.setColor(unpack(_fillcolor))
            ellipse2("fill", x, y, width/2, height/2)
        end
        love.graphics.setLineWidth(_strokewidth)
        love.graphics.setColor(unpack(_strokecolor))
        ellipse2("line", x, y, width/2, height/2)
    end
end

-- Love2d does not have a ellipse function, so we have todo it by ourself
-- a & b are axis-radius
function ellipse2(mode, x, y, a, b)
    local stp=50  -- Step is # of line segments (more is "better")
    local rot=0 -- Rotation in degrees
    local m,rad,sa,ca,sb,cb,x1,y1,ast
    m = math; rad = m.pi/180; ast = rad * 360/stp;
    sb = m.sin(-rot * rad); cb = m.cos(-rot * rad)
    local vertices = {}
    for n = 1, stp do
        sa = m.sin(ast*n) * b
        ca = m.cos(ast*n) * a
        x1 = x + ca * cb - sa * sb
        y1 = y + ca * sb + sa * cb
        table.insert(vertices, x1)
        table.insert(vertices, y1)
    end
    love.graphics.polygon(mode, vertices)
end

function line(x1, y1, x2, y2)
    setContext(_context)
    _invalidateImageData(_context)
    love.graphics.setColor(unpack(_strokecolor))
    if (x1==x2 and y1==y2) then
        love.graphics.point(x1, y1)
    else
        love.graphics.setLineWidth(_strokewidth)
        love.graphics.line(x1, y1, x2, y2)
    end
end

function rect(x, y, width, height)
    setContext(_context)
    _invalidateImageData(_context)
    if _rectmode == CENTER then
        x = x - width / 2
        y = y - height / 2
    elseif _rectmode == RADIUS then
        x = x - width
        y = y - height
        widht = widht * 2
        height = height * 2
    elseif _rectmode == CORNERS then
        -- width and height are the other corner
        if x > width then x, width = width, x end
        if y > height then y, height = height, y end
        width = width - x
        height = height - y
    end
    if _fillmode == "fill" then
        love.graphics.setColor(unpack(_fillcolor))
        love.graphics.rectangle("fill", x, y, width, height)
    end
    local lw = _strokewidth
    love.graphics.setColor(unpack(_strokecolor))
    love.graphics.setLineWidth(_strokewidth)
    love.graphics.rectangle("line",x+lw/2,y+lw/2,width-lw,height-lw)
end

-------------------
-- Transform
-------------------

function translate(dx, dy)
    love.graphics.translate( dx, dy )
end

function rotate(angle)
    love.graphics.rotate(math.rad(angle))
end

function scale(sx, sy)
    love.graphics.scale( sx, sy )
end

-------------------
-- Transform Management
-------------------

function pushMatrix()
    love.graphics.push()
end

function popMatrix()
    love.graphics.pop()
end

function resetMatrix()
    -- TODO
end

-------------------
-- Style Management
-------------------

_style_stack = {}

function popStyle()
    -- TODO
    local style = table.remove(_style_stack)
    _fillcolor = style.fillcolor
    _fillmode = style.fillmode
    _tintcolor = style.tintcolor
    _tint_active = style.tint_active
    ellipseMode(style.ellipsemode)
    rectMode(style.rectmode)
    spriteMode(style.spritemode)
    textMode(style.textmode)
    _strokecolor = style.strokecolor
    strokeWidth(style.strokewidth)
end

function pushStyle()
    local style = {}
    style.fillcolor = _fillcolor
    style.fillmode = _fillmode
    style.tintcolor = _tintcolor
    style.tint_active = _tint_active
    style.strokewidth = _strokewidth
    style.strokecolor = _strokecolor
    style.ellipsemode = _ellipsemode
    style.rectmode = _rectmode
    style.spritemode = _spritemode
    style.ellipsemode = _ellipsemode
    style.textmode = _textmode
    table.insert(_style_stack, style)
end

function resetStyle()
    noFill()
    noTint()
    noStroke()
    stroke(255, 255, 255, 255)
    ellipseMode(CENTER)
    rectMode(CORNER)
    spriteMode(CENTER)
    textMode(CENTER)
end

-------------------
-- Sound
-------------------

function sound(name)
    -- TODO
end

-------------------
-- Style
-------------------

function ellipseMode(mode)
    _ellipsemode = mode
end

function _makeFullColor(red, green, blue, alpha)
    if (red and green and blue and alpha) then
        return red, green, blue, alpha
    elseif (red and green and blue) then
        return red, green, blue, 255
    elseif (type(red) == "number" and type(green) == "number") then
        -- gray, alpha
        return red, red, red, green
    elseif (type(red) == "number") then
        -- gray
        return red, red, red, 255
    elseif red and (red.r and red.g and red.b and red.a) then
        -- color
        return red.r, red.g, red.b, red.a
    end
    return red, green, blue, alpha
end


-- fills elipse & rect
_fillcolor = {}

function fill(red, green, blue, alpha)
    if red == nil then
        return unpack(_fillcolor)
    end
    _fillcolor = {_makeFullColor(red, green, blue, alpha)}
    _fillmode = "fill"
end

function lineCapMode(mode)
    _linecapmode = mode
end

function noSmooth()
    -- TODO
end

function noFill()
    _fillcolor = {0, 0, 0, 0}
    _fillmode = "line"
end

function rectMode(mode)
    _rectmode = mode
end

function smooth()
    -- TODO
end

_strokecolor = {}
_strokewidth = 1

function stroke(red, green, blue, alpha)
    if red == nil then
        return unpack(_strokecolor)
    end
    _strokecolor = {_makeFullColor(red, green, blue, alpha)}
end

function noStroke()
    love.graphics.setLineWidth(0)
    _strokewidth = 0
end

function strokeWidth(width)
    love.graphics.setLineWidth(width)
    _strokewidth = width
end

_tint_active = false
_tintcolor = {}

function tint(red, green, blue, alpha)
    _tint_active = true
    _tintcolor = {_makeFullColor(red, green, blue, alpha)}
end

function noTint()
    _tint_active = false
end

-------------------
-- Parameters
-------------------
function iparameter(name,mini,maxi,initial)
    if initial ~= nil then
        _G[name] = initial
        iparameterList[name] = initial
    else
        _G[name] = mini
        iparameterList[name] = mini
    end
end

function parameter(name,mini,maxi,initial)
    if initial ~= nil then
        _G[name] = initial
        parameterList[name] = initial
    else
        _G[name] = mini
        parameterList[name] = mini
    end
end

function watch(name)
    watchList[name] = 0
end

function iwatch(name)
    iwatchList[name] = 0
end

-------------------
-- Touch
-------------------
-- already done in love.update(dt)

-------------------
-- Math
-------------------
function rsqrt(value)
    return math.pow(value, -0.5);
end

-------------------
-- love functions
-------------------
function love.load()
    _saveInitialState()
    fontSize(17)
    love.graphics.setLine(1, "rough")
    noTint()
    noStroke()
    fill(128, 128, 128, 255)
    stroke(255, 255, 255, 255)
    love.graphics.setColorMode("modulate")
    _setupCurrentContext()
    setup()
    _teardownCurrentContext()
    love.graphics.setCanvas()
end

--[[
-- Love 0.8.0: events are processed BEFORE update()!
-- All touch processing now done in update().
function love.mousepressed(x, y, button)
    if button == "l" then
        CurrentTouch.state = BEGAN
    end
end

function love.mousereleased(x, y, button)
    if button == "l" then
        CurrentTouch.state = ENDED
    end
end
]]--

function love.keypressed(key)
    if keyboard then
        if string.len(key) == 1 then
            if love.keyboard.isDown("lshift", "rshift") then
                key = key:upper()
            end
            keyboard(key)
        elseif key == "backspace" then
            keyboard("")
        end
    end
end

function love.update(dt)

    -- Use sleep to cap FPS at 30
    if dt < 1/30 then
        love.timer.sleep(1/30 - dt)
    end

    -- use Mouse for Touch interaction
    local touch_changed = false
    if love.mouse.isDown("l") then
        -- get Mouse position as Touch position
        -- publish globally
        if CurrentTouch.x ~= love.mouse.getX() or CurrentTouch.y ~= love.mouse.getY() then
            touch_changed = true
        end
        if CurrentTouch.state == ENDED then
            touch_changed = true
        end
        if touch_changed then
            CurrentTouch.prevX = CurrentTouch.x
            CurrentTouch.prevY = CurrentTouch.y
            CurrentTouch.x = love.mouse.getX()
            if MIRROR then
                CurrentTouch.y = HEIGHT - 1 - love.mouse.getY()
            else
                CurrentTouch.y = love.mouse.getY()
            end
            CurrentTouch.y = CurrentTouch.y - _love_win_y_offset
            if CurrentTouch.state == ENDED then
                CurrentTouch.state = BEGAN
                CurrentTouch.prevX = CurrentTouch.x
                CurrentTouch.prevY = CurrentTouch.y
                CurrentTouch.deltaX = 0
                CurrentTouch.deltaY = 0
                CurrentTouch.tapCount = CurrentTouch.tapCount + 1
            else
                CurrentTouch.state = MOVING
                CurrentTouch.deltaX = CurrentTouch.x - CurrentTouch.prevX
                CurrentTouch.deltaY = CurrentTouch.y - CurrentTouch.prevY
            end
        end
    else
        if CurrentTouch.state ~= ENDED then
            CurrentTouch.state = ENDED
            touch_changed = true
        end
    end

    -- has to be outside of mouse.isDown
    if touched and touch_changed then
        -- publish to touched callback
        local touch = {}
        touch.x = CurrentTouch.x
        touch.y = CurrentTouch.y
        touch.prevX  = CurrentTouch.prevX
        touch.prevY  = CurrentTouch.prevY
        touch.deltaX = CurrentTouch.deltaX
        touch.deltaY = CurrentTouch.deltaY
        touch.state  = CurrentTouch.state
        touch.tapCount = CurrentTouch.tapCount
        touch.id = 1
        _setupCurrentContext()
        touched(touch)
        _teardownCurrentContext()
        love.graphics.setCanvas()
    end

    -- use Up,Down,Left,Right Keys to change Gravity
    if love.keyboard.isDown("up") then
        if love.keyboard.isDown("lshift", "rshift") then
            _love_win_y_offset = 300
        else
            Gravity.y = Gravity.y + 0.01
        end
    elseif love.keyboard.isDown("down") then
        if love.keyboard.isDown("lshift", "rshift") then
            _love_win_y_offset = 0
        else
            Gravity.y = Gravity.y - 0.01
        end
    elseif love.keyboard.isDown("left") then
        --Gravity.x = Gravity.x + 0.01
        Gravity.x = Gravity.x - 0.01
    elseif love.keyboard.isDown("right") then
        --Gravity.x = Gravity.x - 0.01
        Gravity.x = Gravity.x + 0.01
    elseif love.keyboard.isDown("pageup") then
        Gravity.z = Gravity.z + 0.01
    elseif love.keyboard.isDown("pagedown") then
        Gravity.z = Gravity.z - 0.01
    end

    -- set Time Values
    DeltaTime = love.timer.getDelta()
    ElapsedTime = love.timer.getTime()
end

function love.draw()
    -- Reset
    if love.keyboard.isDown("return") then
        if not _restarting then
            _restarting = true
            _reset()
        end
        return
    else
        _restarting = false
    end

    _setupCurrentContext()
    draw()
    _teardownCurrentContext()
    love.graphics.setCanvas()

    if (LOVECODEAHUD) then
        local l_fontsize = _fontsize
        fontSize(12)
        love.graphics.setColor( 125, 125, 125)
        love.graphics.print( "iparameter", 5, 14)
        local i=2
        for k,v in pairs(iparameterList) do
            iparameterList[k]=_G[k]
            love.graphics.print( k, 5, 14*i)
            love.graphics.print( tostring(v), 80, 14*i)
            i=i+1
        end

        love.graphics.print( "parameter", 5, 200+14)
        i=2
        for k,v in pairs(parameterList) do
            parameterList[k]=_G[k]
            love.graphics.print( k, 5, 200+14*i)
            love.graphics.print( tostring(v), 80, 200+14*i)
            i=i+1
        end

        love.graphics.print( "watch", 5, 400+14)
        i=2
        for k,v in pairs(watchList) do
            watchList[k]=_G[k]
            love.graphics.print( k, 5, 400+14*i)
            love.graphics.print( tostring(watchList[k]), 80, 400+14*i)
            i=i+1
        end

        love.graphics.print( "iwatch", 5, 600+14)
        i=2
        for k,v in pairs(iwatchList) do
            iwatchList[k]=_G[k]
            love.graphics.print( k, 5, 600+14*i)
            love.graphics.print( tostring(iwatchList[k]), 80, 600+14*i)
            i=i+1
        end

        -- print FPS
        love.graphics.setColor(255,0,0,255)
        love.graphics.print( "FPS: ", WIDTH-65, 14)
        love.graphics.print( love.timer.getFPS( ), WIDTH-35, 14)

        -- print Gravity
        love.graphics.print( "GravityX: ", WIDTH-92, 34)
        love.graphics.print( Gravity.x, WIDTH-35, 34)
        love.graphics.print( "GravityY: ", WIDTH-92, 54)
        love.graphics.print( Gravity.y, WIDTH-35, 54)
        love.graphics.print( "GravityZ: ", WIDTH-92, 74)
        love.graphics.print( Gravity.z, WIDTH-35, 74)

        fontSize(l_fontsize)
    end -- if (LOVECODEAHUD)
end

-- initial before main is called
WIDTH  = love.graphics.getWidth()
HEIGHT = love.graphics.getHeight()

-------------------------
-- loveCodify Internals
-------------------------

_love_win_y_offset = 0

-- deepcopy http://lua-users.org/wiki/CopyTable
function _deepcopy(object)
    local lookup_table = {}
    local function _copy(object)
        if type(object) ~= "table" then
            return object
        elseif lookup_table[object] then
            return lookup_table[object]
        end
        local new_table = {}
        lookup_table[object] = new_table
        for index, value in pairs(object) do
            new_table[_copy(index)] = _copy(value)
        end
        return setmetatable(new_table, getmetatable(object))
    end
    return _copy(object)
end

function _saveInitialState()
    _INIT_STATE = _deepcopy(_G)
    _INIT_STATE["_G"] = nil
end

function _restoreInitialState()
    for k, v in pairs(_INIT_STATE) do
        _G[k] = v
    end
    _saveInitialState(_G)
end

function _reset()
    _restoreInitialState()
    setup()
end

function _mirrorScreenBegin()
    if MIRROR then
        love.graphics.push()
        love.graphics.translate(0, HEIGHT - 1)
        love.graphics.scale(1, -1)
    end
end

function _mirrorScreenEnd()
    if MIRROR then
        love.graphics.pop()
    end
end

function displayMode(mode)
end

function supportedOrientations(orient)
    if orient == PORTRAIT_LEFT or orient == PORTRAIT_ANY or
        orient == PORTRAIT_UPSIDE_DOWN then
        WIDTH  =  768
        HEIGHT = 1024
    else
        WIDTH  = 1024
        HEIGHT =  768
    end
    love.graphics.setMode(WIDTH, HEIGHT)
end

_localdata = {}

function readLocalData(key, default)
    return _localdata[key] or default
end

function saveLocalData(key, value)
    _localdata[key] = value
end

_projectdata = {}

function readProjectData(key, default)
    return _projectdata[key] or default
end

function saveProjectData(key, value)
    _projectdata[key] = value
end

_projectinfo = {}

function readProjectInfo(key, default)
    return _projectinfo[key] or default
end

function saveProjectInfo(key, value)
    _projectinfo[key] = value
end

_globaldata = {}

function readGlobalData(key, default)
    return _globaldata[key] or default
end

function saveGlobalData(key, value)
    _globaldata[key] = value
end

function showKeyboard()
end

function hideKeyboard()
end

--
-- Inlined module: loveCodeaImage.lua
--

-- An image in Codea can be used like a good old pixel-wise image but also as
-- a rendering context.
-- For pixel-wise imaging it yould be enough to keep an ImageData structure
-- around and create an image from it when needed, but ImageData cannot be
-- used to render into.
-- A Canvas can be rendered into and setting individual pixels is possible,
-- just a bit difficult.
-- Therefore a Codea image is represented by a Love Canvas.

image = class()

function image:init(w, h)
    self.c = love.graphics.newCanvas(w, h)
    self.width = w
    self.height = h
    self._imagedata = nil
end

function image:_invalidateImageData()
    self._imagedata = nil
end

function image:get(x, y)
    if self._imagedata == nil then
        self._imagedata = self.c:getImageData()
    end
    return self._imagedata:getPixel(x - 1, self.height - y)
end

function image:set(x, y, r, g, b, a)
    r, g, b, a = _makeFullColor(r, g, b, a)
    -- Set a pixel in a canvas by rendering into it.
    -- There is not truly immediate way of write-accessing a pixel because
    -- getImageData() returns a copy.

    -- This one does not replay transformations
    _setPixelContext(self)
    self._imagedata = nil
    --local currentcanvas = love.graphics.getCanvas()
    --love.graphics.setCanvas(self.c)
    -- Pixel perfect rendering (TODO: remember blend mode? default is alpha)
    local psize = love.graphics.getPointSize()
    local pstyle = love.graphics.getPointStyle()
    love.graphics.setPoint(1, "rough")
    love.graphics.setBlendMode("subtractive")
    love.graphics.setColor(255, 255, 255, 255)
    love.graphics.point(x - 1, y - 1)
    -- now set it
    love.graphics.setBlendMode("premultiplied")
    love.graphics.setColor(r, g, b, a)
    love.graphics.point(x - 1, y - 1)
    love.graphics.setBlendMode("alpha")
    --love.graphics.setPoint(psize, pstyle)
    love.graphics.setPointSize(psize)
    love.graphics.setPointStyle(pstyle)
end

-- untested
function image:copy(x, y, w, h)
    x = x or 1
    y = y or 1
    w = w or self.width
    h = h or self.height
    local newimage = image(w, h)
    for xi = 1, w do for yi = 1, h do
        local r, g, b, a = self:get(x + xi - 1, y + yi - 1)
        newimage:set(xi, yi, r, g, b, a)
    end end
    return newimage
end

function image:_getDrawable()
    return self.c
end

function image:getHeight()
    return self.c:getImageData():getHeight()
end

function image:getWidth()
    return self.c:getImageData():getWidth()
end

--
-- Inlined module: loveCodeaMatrix.lua
--

_transformation_stack = {}
_push_count = 0
_context = nil
_pixel_context = nil

function setContext(context)
    if context ~= _context or _pixel_context ~= nil then
        _teardownCurrentContext()
        _context = context
        _setupCurrentContext()
        _replayTransformations()
        if context == nil then
            love.graphics.setCanvas()
        else
            love.graphics.setCanvas(context.c)
        end
    end
    _pixel_context = nil
end

function _invalidateImageData(context)
    if context ~= nil then
        context:_invalidateImageData()
    end
end

_pixel_context_initialized = false
-- seems to be 1 on Windows and 0 on MacOS (and what on Linux?)
_pixel_context_y_offset = 0

function _initPixelContext()
    local c = love.graphics.newCanvas(2, 2)
    love.graphics.setCanvas(c)
    love.graphics.push()
    love.graphics.translate(0, 2 - 1)
    love.graphics.scale(1, -1)
    local psize = love.graphics.getPointSize()
    local pstyle = love.graphics.getPointStyle()
    love.graphics.setPoint(1, "rough")
    love.graphics.setBlendMode("premultiplied")
    love.graphics.setColor(255, 255, 255, 255)
    love.graphics.point(0, 1)
    love.graphics.setBlendMode("alpha")
    --love.graphics.setPoint(psize, pstyle)
    love.graphics.setPointSize(psize)
    love.graphics.setPointStyle(pstyle)
    local red = c:getImageData():getPixel(0, 0)
    if red == 0 then
        _pixel_context_y_offset = 0
    else
        _pixel_context_y_offset = 1
    end
    love.graphics.pop()
    love.graphics.setCanvas()
    _pixel_context_initialized = true
end
_initPixelContext()

function _setPixelContext(context)
    if _pixel_context ~= context then
        _teardownCurrentContext()
        _pixel_context = context
        --_setupCurrentContext()
        love.graphics.push()
        love.graphics.translate(0, context:getHeight() - _pixel_context_y_offset)
        love.graphics.scale(1, -1)
        for i = 1, _push_count do
            love.graphics.push()
        end
        love.graphics.setCanvas(context.c)
    end
end

function _setupCurrentContext()
    love.graphics.push()
    if _context == nil then
        love.graphics.translate(0, HEIGHT - 1 - _love_win_y_offset)
        love.graphics.scale(1, -1)
    else
        love.graphics.translate(0, _context:getHeight() - 1)
        love.graphics.scale(1, -1)
    end
end

function _teardownCurrentContext()
    for i = 1, _push_count do
        love.graphics.pop()
    end
    love.graphics.pop()
end

function pushMatrix()
    love.graphics.push()
    table.insert(_transformation_stack, {"push"})
    _push_count = _push_count + 1
end

function popMatrix()
    love.graphics.pop()
    _push_count = _push_count - 1
    local do_replay = false
    for i = #_transformation_stack, 1, -1 do
        local t = table.remove(_transformation_stack)
        if t[1] == "reset" then
            do_replay = true
        elseif t[1] == "push" then
            break
        end
    end
    if do_replay then
        for i = 1, _push_count do
            love.graphics.pop()
        end
        _replayTransformations()
    end
end

function resetMatrix()
    _popTransformationStackUntilPush()
    _teardownCurrentContext()
    _setupCurrentContext()
    for i = 1, _push_count do
        love.graphics.push()
    end
    table.insert(_transformation_stack, {"reset"})
end

-- Pops transformations from the stack until it finds a push, not popping
-- the push itself.
function _popTransformationStackUntilPush()
    for i = #_transformation_stack, 1, -1 do
        local t = _transformation_stack[i]
        if t[1] == "push" then
            return
        end
        table.remove(_transformation_stack)
    end
end

-- Replays (executes) all transformations on the transformation stack.
-- Pushes are executed but not counted, the push count was and is always
-- the current push count.
function _replayTransformations()
    for i = 1, #_transformation_stack do
        local t = _transformation_stack[i]
        if t[1] == "push" then
            love.graphics.push()
        elseif t[1] == "translate" then
            love.graphics.translate(t[2], t[3])
        elseif t[1] == "rotate" then
            love.graphics.rotate(math.rad(t[2]))
        elseif t[1] == "scale" then
            love.graphics.scale(t[2], t[3])
        end
    end
end

function translate(dx, dy)
    love.graphics.translate(dx, dy)
    table.insert(_transformation_stack, {"translate", dx, dy})
end

function rotate(angle)
    love.graphics.rotate(math.rad(angle))
    table.insert(_transformation_stack, {"rotate", angle})
end

function scale(sx, sy)
    love.graphics.scale(sx, sy)
    table.insert(_transformation_stack, {"scale", sx, sy})
end

--
-- Inlined module: loveCodeaMesh.lua
--

mesh = class()

function mesh:init()
    self.vertices = {}
    self.rectlist = {}
    self.recttexlist = {}
    self.rectcolorlist = {}
    self.texture = nil
    self._oldtexture = nil
    self._image = nil
    self.colors = color(255, 255, 255, 255)
end

function mesh:setColors(c)
    self.colors = c
end

function mesh:addRect(x, y, w, h, r)
    r = r or 0
    table.insert(self.rectlist, {x, y, w, h, r})
    return #self.rectlist
end

function mesh:setRect(i, x, y, w, h, r)
    r = r or 0
    self.rectlist[i] = {x, y, w, h, r}
end

function mesh:setRectTex(i, s, t, w, h)
    self.recttexlist[i] = {s, t, w, h}
end

function mesh:setRectColor(i, c)
    self.rectcolorlist[i] = c
end

function mesh:draw()
    self:_drawTriangles()
    self:_drawRectangles()
end

function mesh:_textureToImage()
    if self.texture ~= self._oldtexture then
        if self.texture == nil then
            self._image = nil
        elseif type(self.texture) == "string" then
            self._image = _loadSprite(self.texture)
        else
            self._image = self.texture
        end
        self._oldtexture = self.texture
    end
end

function mesh:_drawRectangles()
    for i = 1,#self.rectlist do
    local r = self.rectlist[i]
    local x, y, w, h, rot = r[1], r[2], r[3], r[4], r[5]
    local c = self.rectcolorlist[i]
    love.graphics.push()
    if c ~= nil then
        love.graphics.setColor(c.r, c.g, c.b, c.a)
    end
    if self.texture == nil then
        love.graphics.translate(x ,y)
        love.graphics.rotate(rot)
        love.graphics.rectangle("fill", - w / 2, - h / 2, w, h)
    else
        -- texture coordinates and scales
        local tex_x, tex_y, tex_xs, tex_ys = 0, 0, 1, 1
        if self.recttexlist[i] ~= nil then
            local rt = self.recttexlist[i]
            tex_x, tex_y, tex_xs, tex_ys = rt[1], rt[2], rt[3], rt[4]
        end
        self:_textureToImage()
        local drawable
        local image_w
        local image_h
        if self._image ~= nil then
            drawable = self._image
            if drawable._getDrawable == nil then
                image_w = drawable:getWidth()
                image_h = drawable:getHeight()
            else
                drawable = drawable:_getDrawable()
                image_w = drawable:getImageData():getWidth()
                image_h = drawable:getImageData():getHeight()
            end
        end
        local xscale = w / image_w
        local yscale = h / image_h
        love.graphics.translate(x ,y)
        love.graphics.rotate(rot)
        -- mini implementation for texture directions -1 / -1 (Space Puzzle 8)
        local x0 = 0
        local y0 = h
        if tex_xs < 0 then
            x0 = w
            xscale = xscale * tex_xs
        end
        if tex_ys < 0 then
            y0 = 0
            yscale = yscale * tex_ys
        end
        if c ~= nil then
            love.graphics.setColorMode("modulate")
        else
            love.graphics.setColorMode("replace")
        end
        love.graphics.draw(drawable, x0 - w / 2, y0 - h / 2, 0, xscale, -yscale)
        love.graphics.setColorMode("modulate")
    end
    love.graphics.pop()
    end
end

function mesh:_drawTriangles()
    local c = self.colors
    local ncolors = 0
    if c ~= nil then
        if c.r ~= nil then
            ncolors = 1
            love.graphics.setColor(c.r, c.g, c.b, c.a)
        else
            ncolors = #c
        end
    end
    local v = self.vertices
    local nvertices = #v
    for i = 1,nvertices,3 do
        local x1, y1 = v[i]:unpack()
        local x2, y2 = v[i + 1]:unpack()
        local x3, y3 = v[i + 2]:unpack()
        if ncolors > 1 then
            mesh:_drawTriangle(x1, y1, c[i], x2, y2, c[i + 1], x3, y3, c[i + 2])
        else
            love.graphics.triangle("fill", x1, y1, x2, y2, x3, y3)
        end
    end
end

-- Draw rasterized triangles.
-- Usually, rasterized triangles are drawn line by line in steps of 1.
-- If triangles are drawn this way and the matrix is rotated, it will have
-- empty spots, therefore the rasterizer advances in steps of 2, drawing
-- points with a size of 3 for a slight overlap.
-- (This also improves the poor rendering speed, steps of 1 and a point size
-- of 2 would take much longer.)

function mesh:_drawTriangle(x1, y1, c1, x2, y2, c2, x3, y3, c3)
    local dy12 = math.abs(y2 - y1)
    local dy23 = math.abs(y3 - y2)
    local dy31 = math.abs(y1 - y3)
    local startx, starty, startc
    local stopx, stopx, stopc
    local othx, othy, othc

    if dy12 > dy23 and dy12 > dy31 then
        -- v1 and v2 span the largest vertical distance
        startx, starty, startc = x1, y1, c1
        stopx, stopy, stopc = x2, y2, c2
        othx, othy, othc = x3, y3, c3
    elseif dy23 > dy31 then
        -- v2 and v3 span the largest vertical distance
        startx, starty, startc = x2, y2, c2
        stopx, stopy, stopc = x3, y3, c3
        othx, othy, othc = x1, y1, c1
    else
        -- v3 and v1 span the largest vertical distance
        startx, starty, startc = x3, y3, c3
        stopx, stopy, stopc = x1, y1, c1
        othx, othy, othc = x2, y2, c2
    end

    if starty > stopy then
        startx, stopx = stopx, startx
        starty, stopy = stopy, starty
        startc, stopc = stopc, startc
    end

    -- Go from starty to stopy, interpolating the color from startc to stopc.
    -- This is the main slope.
    -- For every vertical position on the main slope draw a line from the
    -- current position and color to the other slope.
    -- The other slope is split into two parts, from start to other and from
    -- other to stop.

    -- m.  main
    -- md. main deltas
    -- mi. main increments
    -- o.  other
    -- od. other deltas
    -- oi. other increments
    local mx, my, mr, mg, mb, ma = startx, starty, startc.r, startc.g, startc.b, startc.a
    local mdx = stopx - startx
    local mdy = stopy - starty
    local mix = mdx / mdy
    local mir = (stopc.r - mr) / mdy
    local mig = (stopc.g - mg) / mdy
    local mib = (stopc.b - mb) / mdy
    local mia = (stopc.a - ma) / mdy

    --local ox, oy, o_r, og, ob, oa = othx, othy, othc.r, othc.g, othc.b, othc.a
    local ox, o_r, og, ob, oa = startx, startc.r, startc.g, startc.b, startc.a
    local odx = othx - startx
    local ody = othy - starty
    local oix = odx / ody
    local oir = (othc.r - startc.r) / ody
    local oig = (othc.g - startc.g) / ody
    local oib = (othc.b - startc.b) / ody
    local oia = (othc.a - startc.a) / ody

    while my < stopy do
        if my == othy then
            -- First "other" slope done, prepare for the second "other" slope
            ox, o_r, og, ob, oa = othx, othc.r, othc.g, othc.b, othc.a
            odx = stopx - othx
            ody = stopy - othy
            oix = odx / ody
            oir = (stopc.r - othc.r) / ody
            oig = (stopc.g - othc.g) / ody
            oib = (stopc.b - othc.b) / ody
            oia = (stopc.a - othc.a) / ody
        end
        mc = color(mr, mg, mb, ma)
        oc = color(o_r, og, ob, oa)
        mesh:_drawHLine(mx, ox, my, mc, oc)
        my = my + 2
        mx = mx + mix * 2
        ox = ox + oix * 2
        mr = mr + mir * 2
        mg = mg + mig * 2
        mb = mb + mib * 2
        ma = ma + mia * 2
        o_r = o_r + oir * 2
        og = og + oig * 2
        ob = ob + oir * 2
        oa = oa + oia * 2
    end
end

-- Draws a horizontal line with color interpolation.
-- Used for rasterizing triangles.
function mesh:_drawHLine(x1, x2, y, c1, c2)
    local psize = love.graphics.getPointSize()
    local pstyle = love.graphics.getPointStyle()
    if x1 > x2 then
        x1, x2 = x2, x1
        c1, c2 = c2, c1
    end
    local dx = x2 - x1
    if dx < 1 then dx = 1 end
    local r, g, b, a = c1.r, c1.g, c1.b, c1.a
    local dr, dg, db, da
    ir = (c2.r - c1.r) / dx
    ig = (c2.g - c1.g) / dx
    ib = (c2.b - c1.b) / dx
    ia = (c2.a - c1.a) / dx
    love.graphics.setPoint(3, "rough")
    while x1 < x2 do
        if r > 255 then r = 255 end
        if g > 255 then g = 255 end
        if b > 255 then b = 255 end
        if a > 255 then a = 255 end
        if r < 0 then r = 0 end
        if g < 0 then g = 0 end
        if b < 0 then b = 0 end
        if a < 0 then a = 0 end
        love.graphics.setColor(r, g, b, a)
        love.graphics.point(x1, y)
        x1 = x1 + 2
        r = r + ir * 2
        g = g + ig * 2
        b = b + ib * 2
        a = a + ia * 2
    end
    --love.graphics.setPoint(psize, pstyle)
    love.graphics.setPointSize(psize)
    love.graphics.setPointStyle(pstyle)
end

--
-- Inlined module: loveCodeaNoise.lua
--

-- Lua version of code in ImprovedPerlinNoise.h of the Codea Runtime

_p = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,151,
160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,
37,240,21,10,23,190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,
11,32,57,177,33,88,237,149,56,87,174, 20,125,136, 171,168, 68,175,74,165,71,
134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
55,46,245,40,244,102,143,54,65,25,63,161, 1,216,80,73,209,76,132,187,208,89,
18, 169,200,196,135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,
226,250, 124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,
17,182,189,28,42,223,183,170,213,119,248,152, 2,44, 154,163, 70,221,153,101,
155,167, 43,172,9,129,22,39,253, 19,98,108,110,79, 113,224, 232,178,185,112,
104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,
235,249,14,239,107,49,192,214, 31,181,199,106,157,184, 84, 204, 176,115,121,
50,45,127, 4,150,254,138,236,205,93,222,114,67,29,24,72,243,141, 128,195,78,
66,215,61,156,180
}

function _perlin_fade(t)
    return t * t * t * (t * (t * 6 - 15) + 10)
end

function _perlin_lerp(t, a, b)
    return a + t * (b - a)
end

function _perlin_grad(hash, x, y, z)
    local floor = math.floor
    local mod = math.mod
    local h = floor(mod(hash, 16))
    local u
    local v
    if h < 8 then u = x else u = y end
    if h < 4 then v = y elseif h == 12 or h == 14 then v = x else v = z end
    local r
    if floor(mod(h, 2)) == 0 then r = u else r = -u end
    h = floor(h / 2)
    if floor(mod(h, 2)) == 0 then r = r + v else r = r - v end
    return r
end

function _perlin_noise(x, y, z)
    local abs = math.abs
    local floor = math.floor
    local mod = math.mod
    local X = abs(floor(mod(x, 256)))
    local Y = abs(floor(mod(y, 256)))
    local Z = abs(floor(mod(z, 256)))
    x = mod(x, 1)
    y = mod(y, 1)
    z = mod(z, 1)

    local u = _perlin_fade(x)
    local v = _perlin_fade(y)
    local w = _perlin_fade(z)

    local A  = _p[X + 1] + Y
    local AA = _p[A + 1] + Z
    local AB = _p[A + 1 + 1] + Z
    local B  = _p[X + 1 + 1] + Y
    local BA = _p[B + 1] + Z
    local BB = _p[B + 1 + 1] + Z

    return _perlin_lerp(w,
        _perlin_lerp(v,
        _perlin_lerp(u, _perlin_grad(_p[AA   +1], x  , y  , z),
                        _perlin_grad(_p[BA   +1], x-1, y  , z)),
        _perlin_lerp(u, _perlin_grad(_p[AB   +1], x  , y-1, z),
                        _perlin_grad(_p[BB   +1], x-1, y-1, z))),
        _perlin_lerp(v,
        _perlin_lerp(u, _perlin_grad(_p[AA+1 +1], x  , y  , z-1),
                        _perlin_grad(_p[BA+1 +1], x-1, y  , z-1)),
        _perlin_lerp(u, _perlin_grad(_p[AB+1 +1], x  , y-1, z-1),
                        _perlin_grad(_p[BB+1 +1], x-1, y-1, z-1))))
end

function noise(x, y, z)
    x = x or 0
    y = y or 0
    z = z or 0
    return _perlin_noise(x, y, z)
end

--
-- Inlined module: loveCodeaSprite.lua
--

_spritemode = CENTER
_sprite_list = {}

function spriteMode(mode)
    _spritemode = mode
end

-- Load & Register Sprite and Draw it
function sprite(name_or_image, x, y, width, height)
    x = x or 0
    y = y or 0
    if type(name_or_image) == "string" then
        _loadSprite(name_or_image)
        _spriteDraw(_sprite_list[name_or_image], x, y, width, height)
    else
        _spriteDraw(name_or_image, x, y, width, height )
    end
end

function spriteSize(filename)
    _loadSprite(filename)
    local img = _sprite_list[filename]
    local w = img:getWidth()
    local h = img:getHeight()
    return w, h
end

function _loadSprite(filename)
    if _sprite_list[filename] == nil then
        local realname1 = filename:gsub("\:",".spritepack/") .. ".png"
        local realname2 = filename:gsub("\:","/") .. ".png"
        if love.filesystem.isFile(realname1) then
            _sprite_list[filename] = love.graphics.newImage(realname1)
        else
            _sprite_list[filename] = love.graphics.newImage(realname2)
        end
    end
    return _sprite_list[filename]
end

function _spriteDraw(image, x, y, width, height)
    setContext(_context)
    _invalidateImageData(_context)
    -- image dimensions
    local w = image:getWidth()
    local h = image:getHeight()
    -- image dimension scale factors
    local sx = 1
    local sy = 1
    if width ~= nil then
        sx = width / w
        -- scale height properly if only width is given
        if height == nil then
            sy = sx
            height = h * sy
        end
    end
    if height ~= nil then
        sy = height / h
    end
    width = width or w
    height = height or h

    if _tint_active then
        love.graphics.setColorMode("modulate")
        love.graphics.setColor(unpack(_tintcolor))
    else
        love.graphics.setColorMode("replace")
    end
    x = x or 0
    y = y or 0
    if _spritemode == CORNERS then
        if x > width then
            x, width = width, x
        end
        if y < height then
            y, height = height, y
        end
    end
    love.graphics.push()
    love.graphics.translate(x, y)
    love.graphics.scale(1, -1)
    if image._getDrawable ~= nil then
        image = image:_getDrawable()
    end
    if _spritemode == CENTER then
        love.graphics.draw(image, -width / 2, -height / 2, 0, sx, sy)
    elseif _spritemode == CORNERS then
        sx = (width - x) / w
        sy = (y - height) / h
        love.graphics.draw(image, 0, 0, 0, sx, sy)
    else
        -- CORNER (upper left)
        love.graphics.draw(image, 0, 0, 0, sx, sy)
    end
    love.graphics.pop()
    love.graphics.setColorMode("modulate")
end

--
-- Inlined module: loveCodeaText.lua
--

_textmode = CENTER

function text(str, x, y)
    x = x or 0
    y = y or 0
    setContext(_context)
    love.graphics.push()
    local f = love.graphics.getFont()
    local w = f:getWidth(str)
    local h = f:getHeight()
    love.graphics.setColor(unpack(_fillcolor))
    love.graphics.translate(x, y)
    love.graphics.scale(1, -1)
    if _textmode == CENTER then
        love.graphics.print(str, -w / 2, -h / 2)
    else
        -- CORNER (lower left)
        love.graphics.print(str, 0, -h)
    end
    love.graphics.pop()
end

function textSize(str)
    local f = love.graphics.getFont()
    local w = f:getWidth(str)
    local h = f:getHeight()
    return w, h
end

function textMode(mode)
    _textmode = mode
end

function textWrapWidth(w)
end

function textAlign()
end

_fontcache = {}
_fontname = ""
_fontsize = 17

function fontSize(size)
    local fullname = _fontname .. size
    if _fontcache[fullname] == nil then
        if _fontname == "" then
            _fontcache[fullname] = love.graphics.newFont(size)
        else
            _fontcache[fullname] = love.graphics.newFont(_fontname, size)
        end
    end
    love.graphics.setFont(_fontcache[fullname])
    _fontsize = size
end

function font(fontname)
    local ttf = fontname .. ".ttf"
    if love.filesystem.isFile(ttf) then
        _fontname = ttf
    else
        _fontname = ""
    end
end

function fontMetrics()
    local met = {}
    met.ascent = _fontsize * 0.9
    met.descent = _fontsize * 0.1
    met.leading = 0
    met.xHeight = 0
    met.capHeight = 0
    met.underlinePosition = 0
    met.underlineThickness = 0
    met.slantAngle = 0
    return met
end

--
-- Inlined module: loveCodeaVector.lua
--

--[[
Copyright (c) 2010 Matthias Richter

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
]]--

local assert = assert
local sqrt, cos, sin = math.sqrt, math.cos, math.sin

local vector = {}
vector.__index = vector

local function new(x,y)
	local v = {x = x or 0, y = y or 0}
	setmetatable(v, vector)
	return v
end

local function isvector(v)
	return getmetatable(v) == vector
end

function vector:clone()
	return new(self.x, self.y)
end

function vector:unpack()
	return self.x, self.y
end

function vector:__tostring()
	return "("..tonumber(self.x)..","..tonumber(self.y)..")"
end

function vector.__unm(a)
	return new(-a.x, -a.y)
end

function vector.__add(a,b)
	assert(isvector(a) and isvector(b), "Add: wrong argument types (<vector> expected)")
	return new(a.x+b.x, a.y+b.y)
end

function vector.__sub(a,b)
	assert(isvector(a) and isvector(b), "Sub: wrong argument types (<vector> expected)")
	return new(a.x-b.x, a.y-b.y)
end

function vector.__mul(a,b)
	if type(a) == "number" then
		return new(a*b.x, a*b.y)
	elseif type(b) == "number" then
		return new(b*a.x, b*a.y)
	else
		assert(isvector(a) and isvector(b), "Mul: wrong argument types (<vector> or <number> expected)")
		return a.x*b.x + a.y*b.y
	end
end

function vector.__div(a,b)
	assert(isvector(a) and type(b) == "number", "wrong argument types (expected <vector> / <number>)")
	return new(a.x / b, a.y / b)
end

function vector.__eq(a,b)
	return a.x == b.x and a.y == b.y
end

function vector.__lt(a,b)
	return a.x < b.x or (a.x == b.x and a.y < b.y)
end

function vector.__le(a,b)
	return a.x <= b.x and a.y <= b.y
end

function vector.permul(a,b)
	assert(isvector(a) and isvector(b), "permul: wrong argument types (<vector> expected)")
	return new(a.x*b.x, a.y*b.y)
end

function vector:lenSqr()
	return self.x * self.x + self.y * self.y
end

function vector:len()
	return sqrt(self:lenSqr())
end

function vector.dist(a, b)
	assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
	return (b-a):len()
end

function vector:normalize_inplace()
	local l = self:len()
	self.x, self.y = self.x / l, self.y / l
	return self
end

function vector:normalized()
	return self / self:len()
end

--added for codea
function vector:normalize()
	return self / self:len()
end

function vector:rotate_inplace(phi)
	local c, s = cos(phi), sin(phi)
	self.x, self.y = c * self.x - s * self.y, s * self.x + c * self.y
	return self
end

function vector:rotated(phi)
	return self:clone():rotate_inplace(phi)
end

-- added for codea
function vector:rotate(phi)
	return self:clone():rotate_inplace(phi)
end

function vector:perpendicular()
	return new(-self.y, self.x)
end

function vector:projectOn(v)
	assert(isvector(v), "invalid argument: cannot project onto anything other than a vector")
	return (self * v) * v / v:lenSqr()
end

function vector:mirrorOn(other)
	assert(isvector(other), "invalid argument: cannot mirror on anything other than a vector")
	return 2 * self:projectOn(other) - self
end

function vector:cross(other)
	assert(isvector(other), "cross: wrong argument types (<vector> expected)")
	return self.x * other.y - self.y * other.x
end

-- added for codea
-- like in mul
function vector:dot(other)
	assert(isvector(other), "dot: wrong argument types (<vector> expected)")
	return self.x * other.x + self.y * other.y
end

function vector:angleBetween(other)
	assert(isvector(other), "angleBetween: wrong argument types (<vector> expected)")
	local alpha1 = math.atan2(self.y, self.x)
	local alpha2 = math.atan2(other.y, other.x)
	return alpha2 - alpha1
end

-- added for codea
function vec2(x,y)
	return new(x,y)
end
-- added for codea
function vec3(x,y,z)
	return new(x,y)
end

-- the module
--return setmetatable({new = new, isvector = isvector},
--	{__call = function(_, ...) return new(...) end})

Updated