Wiki
Clone wikiCore / 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:
- run many examples like Cargo-Bot and others found on the Codea Forum;
- start an small file server and browse your desktop files on the iPad; and
- 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