Skip to content

Instantly share code, notes, and snippets.

@XanDDemoX
Created June 30, 2013 01:28
Show Gist options
  • Save XanDDemoX/5893398 to your computer and use it in GitHub Desktop.
Save XanDDemoX/5893398 to your computer and use it in GitHub Desktop.
Codea Project Gist Created with AutoGist
AutoGist Tab Order Version: 2.1.5
------------------------------
This file should not be included in the Codea project.
#Floor
#Main
#Scene3D
#SceneCamera
#Tower
-------------------------------------------------------------------------------------------
-- SceneCamera --
-- A library which moves the camera and alters perspective --
-- by animating them with tween, tween.path and tween.sequence --
-- for cinematic effects or any other movement you can think of --
-- Version:1.0.3 --
-- Written by: XanDDemoX --
-------------------------------------------------------------------------------------------
Floor = class()
function Floor:init()
-- you can accept and set parameters here
end
function Floor:draw()
-- Codea does not automatically call this method
-- Make a floor (borrowed from one of the example projects)
rotate(90,1,0,0)
sprite("SpaceCute:Background", 0, 0, 300, 300)
end
function Floor:touched(touch)
-- Codea does not automatically call this method
end
-------------------------------------------------------------------------------------------
-- SceneCamera --
-- A library which moves the camera and alters perspective --
-- by animating them with tween, tween.path and tween.sequence --
-- for cinematic effects or any other movement you can think of --
-- Version:1.0.3 --
-- Written by: XanDDemoX --
-------------------------------------------------------------------------------------------
-- Use this function to perform your initial setup
function setup()
saveProjectInfo("Description","SceneCamera - An Animated 3D camera which uses the built in tween library")
saveProjectInfo("Author","XanDDemoX")
--setup a 3d scene - the camera is created in Scene3D
scene = Scene3D()
-- callback to print the finished messages
local rs =function() tween.resetAll() scene:reset() end
local completedCallback = function(...) print(...) end
-- SceneCamera is a use of the built in perspective and camera functions
-- in combination with the built in tween library allowing simple but powerful
-- custom camera movements which appear smooth because it is animated to new positions
-- easing is mostly compatible, however it appears tween.easing.bounce and tween.easing.elastic
-- and thier derrivatives do not work currently
-- camera movement functions create functions which are used to move the camera
-- so calling just move the camera wont move until the function it creates is called
--
-- SceneCamera:move(time,target,options. returns function(callback,...) end
--
-- Creates a function which when called creates a tween from the cameras current position to
-- the target. Options for the tween can also be optionally specified.
-- to use the callback pass callback and parameters when calling the created function
--
-- SceneCamera:path(time,path,options). returns function(callback,...) end
--
-- Creates a function which when called creates a tween.path from the cameras current position to
-- the end of the specified path. Options for the tween can also be optionally specified.
-- to use the callback pass callback and parameters when calling the created function
-- SceneCamera:sequence(...). returns function(callback,...) end
--
-- Creates a function which when called creates a tween.sequence of the specified movements
-- to use the callback pass callback and parameters when calling the created function
--
-- SceneCamera:seq(...) returns function(callback,...) end
-- shortcut for calling sequence
--
--
-- SceneCamera:reset(time) returns function(callback,...) end
-- creates a function which resets the camera to the position it was initialised with using either
-- a tween or immediately if no time is specfied
-- supported movement parameters for all movement functions
-- eye - vec3 - camera eye
-- centre - vec3 - camera centre
-- up - vec3 - camera up
-- fov - int / float - perspective fov
-- aspect - int / float - aspect ratio
-- size - int / float - size / zoom
-- angle - int / float - rotation around origin
-- sequence function specific additional parameters
-- time - int /float - duration of the movement
-- before- int / float - tween.delay before
-- after - int/ float- tween.delay after
-- callback - function - callback function
-- args - table - args to pass to callback
-- options - table of options (easing and loop)
-- e.g options={easing=tween.easing.cubicIn, loop=tween.loop.pingpong}
-- single movement example
local move = scene.camera:move(4,{angle=360,eye={y=600}},{easing=tween.easing.backIn})
parameter.action("Move",function()
rs()
local mid,m = move(completedCallback,"Single Movement finished")
print("Move Started: returned id == wrapped id",mid==m.id," ",m)
-- the wrapped id (s) returned can stop and reset the movement
--m:stop()
--m:reset()
end)
local path = scene.camera:path(3,{{angle=180}, {eye = {x=360}} })
parameter.action("Path",function()
rs()
local pid,p=path(completedCallback,"Path Movement Completed")
print("Path Started: returned id == wrapped id",pid==p.id," ",p)
-- the wrapped id returned (p) can stop and reset the path
--p:stop()
--p:reset()
end)
-- simple example sequence
seq = scene.camera:sequence({before=1,time=2,after=1,eye={x=180,y=300},centre={x=20,y=20}},
{time=2,eye={x=0,y=300},centre={x=180,y=50},options={easing=tween.easing.expoIn}},
{time=2,eye={x=0,y=0}, centre={x=0,y=0}})
-- simple example sequence move button
parameter.action("Sequence",function()
rs()
local sid,s=seq(completedCallback,"Sequence Movement Completed")
print("Sequence started: returned id == wrapped id",sid==s.id," ",s)
-- the wrapped id returned (s) can stop and reset the sequence
--s:stop()
--s:reset()
end)
parameter.action("Reset",function()rs() end)
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
-- This sets the line thickness
strokeWidth(5)
scene:draw()
end
-- see Scene3D for touch example
function touched(touch)
scene:touched(touch)
end
-------------------------------------------------------------------------------------------
-- SceneCamera --
-- A library which moves the camera and alters perspective --
-- by animating them with tween, tween.path and tween.sequence --
-- for cinematic effects or any other movement you can think of --
-- Version:1.0.3 --
-- Written by: XanDDemoX --
-------------------------------------------------------------------------------------------
Scene3D = class()
function Scene3D:init()
-- setup a scene for seeing the camera move
self.floor = Floor()
-- setup camera with defaults
-- optionall specfiy different initial values in the constructor in a table
-- e.g SceneCamera({fov=55, angle=60 })
self.camera = SceneCamera({angle=0,fov=50})
self.angle = self.camera.view.angle
self.fov = self.camera.view.fov
self.limits = {
minFov = 20,
maxFov = 100
}
end
-- resets the camera and current fov and angle
function Scene3D:reset()
scene.camera:reset()()
self.angle = self.camera.view.angle
self.fov = self.camera.view.fov
end
function Scene3D:draw()
-- Codea does not automatically call this method
-- call the cameras draw method to set the position
self.camera:draw()
-- draw the scene
self.floor:draw()
-- set back to normal 2d drawing
ortho()
viewMatrix(matrix())
end
-- touch example
function Scene3D:touched(touch)
-- Codea does not automatically call this method
if touch.state == MOVING then
-- determine which way left/right or up/down
if (touch.deltaX > 0 and touch.deltaX > touch.deltaY) or
(touch.deltaX < 0 and touch.deltaX < touch.deltaY) then
-- increment or decrement the angle roughly by delta
self.angle = self.angle + (touch.deltaX / 10) * 3
else
-- increment or decrement the fov roughly by delta
self.fov = self.fov + (touch.deltaY / 10) * 3
-- limit fov
if self.fov > self.limits.maxFov then
self.fov = self.limits.maxFov
elseif self.fov < self.limits.minFov then
self.fov = self.limits.minFov
end
end
-- stop the current movement
if self.curMove ~= nil then
self.curMove:stop()
self.curMove = nil
end
-- prepare and start camera movement
local id
id,self.curMove=self.camera:move(0.3,{angle=self.angle,fov=self.fov})()
end
end
-------------------------------------------------------------------------------------------
-- SceneCamera --
-- A library which moves the camera and alters perspective --
-- by animating them with tween, tween.path and tween.sequence --
-- for cinematic effects or any other movement you can think of --
-- Version:1.0.3 --
-- Written by: XanDDemoX --
-------------------------------------------------------------------------------------------
SceneCamera = class()
-- default setup of camera and perspective
SceneCamera.default = {
eye=vec3(0,300,-300),
centre=vec3(0,0,0),
up =vec3(0,1,0),
fov=45,
size=150,
angle=0,
options={easing = tween.easing.linear,
loop = tween.loop.once}
}
-- creates a new scene camera with optional initial values contained witin a table passed on construction
-- eye={x=5,y=200,z=50},centre={x=10,y=20,z=30},up={x=20,y=50,z=70},fov=35,aspect=600/500,
-- options={easing = tween.easing.linear,loop = tween.loop.once}}
function SceneCamera:init(tbl)
tbl = tbl or {}
-- setup parameters from table or defaults
self.view = {}
self.view.eye = SceneCamera.syncvec3(tbl.eye,SceneCamera.default.eye) or
SceneCamera.copyvec3(SceneCamera.default.eye)
self.view.centre =SceneCamera.syncvec3(tbl.centre,SceneCamera.default.centre) or
SceneCamera.copyvec3(SceneCamera.default.centre)
self.view.up = SceneCamera.syncvec3(tbl.up,SceneCamera.default.up) or
SceneCamera.copyvec3(SceneCamera.default.up)
self.view.fov= tbl.fov or SceneCamera.default.fov
self.view.aspect= tbl.aspect or WIDTH/HEIGHT
self.view.size = tbl.size or SceneCamera.default.size
self.view.angle = tbl.angle or SceneCamera.default.angle
self.options = tbl.options or {}
self.options.easing = self.options.easing or SceneCamera.default.options.easing
self.options.loop = self.options.loop or SceneCamera.default.options.loop
self.initial = SceneCamera.copyview(self.view)
end
-- sets the camera and perspective
function SceneCamera:draw()
-- set perspective
perspective(self.view.fov, self.view.aspect)
-- set camera pos
camera(self.view.eye.x,
self.view.eye.y,
self.view.eye.z,
self.view.centre.x,
self.view.centre.y,
self.view.centre.z,
self.view.up.x,
self.view.up.y,
self.view.up.z)
-- set size and rotation
translate(0,-self.view.size/2,0)
rotate(self.view.angle,0,1,0)
end
-- creates a function to perform a tween.sequence when the returned function is called
-- when the returned function is called it returns the tween id and a table which wraps the id
-- with a stop and reset function.
---------------------------------------
-- call sequence with all required movements. with time, delays before and after, callbacks and args.
--{time=2, callback = function() end, args={} before=1,after=1,
--eye={x=5,y=200,z=50},centre={x=10,y=20,z=30},up={x=20,y=50,z=70},fov=35,aspect=600/500},
-- {eye={x=10,y=300,z=50},centre={x=10,y=20,z=30},up={x=20,y=50,z=70},fov=35,aspect=600/500}}
-- options - any custom options other than the instances default
-- returns a function(callback,...) end which calls a tween.sequence and returns the id and a table
-- which wraps the id with a stop and reset function.
--if a callback and args is passed its called on completion of the sequence
function SceneCamera:sequence(...)
path = Tower(arg)
local tbl = Tower()
for k,v in path:items() do
v.time = v.time or 1
v.args = v.args or {}
v.options = v.options or {}
v.options.easing = self.options.easing
v.options.loop = self.options.loop
-- set delay before if specified
if v.before ~= nil and v.before > 0 then
tbl:push(function() return tween.delay(v.before) end)
end
-- set movement
tbl:push(function()
return self:move(v.time,{eye = v.eye,centre= v.centre,up= v.up,
fov=v.fov,aspect = v.aspect,size =v.size,angle = v.angle},v.options)(v.callback,unpack(v.args))
end)
-- set delay after if specfied
if v.after ~= nil and v.after > 0 then
tbl:push(function() return tween.delay(v.after) end)
end
end
return function(cb,...)
if cb ~= nil and path:count() > 0 then
local args = arg
local i = path:item(path:count())
local new =-1
-- setup correct function for callback
if i.after ~= nil and i.after > 0 then
new = function() return tween.delay(i.after,cb,unpack(args)) end
else
new = function() return self:move(i.time,{
eye=i.eye,centre = i.centre,up =i.up,fov = i.fov,
aspect = i.aspect,angle = i.angle,size = i.size},i.options)(cb,unpack(args)) end
end
-- bin the original function without a callbck and add the new one
tbl:pop()
tbl:push(new)
end
local id = tween.sequence(unpack(tbl:map(function(tw)
local id = tw()
return id end)))
return id,SceneCamera.wrap(id)
end
end
-- shortcut method for sequence
function SceneCamera:seq(...)
return self:sequence(...)
end
-- creates a function to perform a movement of the camera with tween.path
-- when the returned function is called it returns the tween id and a table which wraps the id
-- with a stop and reset function.
---------------------------------------
-- time - the time span of the movement
-- path - the path to animate from the current position using one or more of the options below
-- e.g {{eye={x=5,y=200,z=50},centre={x=10,y=20,z=30},up={x=20,y=50,z=70},fov=35,aspect=600/500},
-- {eye={x=10,y=300,z=50},centre={x=10,y=20,z=30},up={x=20,y=50,z=70},fov=35,aspect=600/500}}
-- options - any custom options other than the instances default
-- returns a function(callback,...) end which calls a tween.path and returns the id and a table
-- which wraps the id with a stop and reset function.
--if a callback and args is passed its called on completion of the path
function SceneCamera:path(time,path,options)
if time == nil then return end
path = path or {}
-- set defaults or values
for k,v in pairs(path) do
v.eye = SceneCamera.syncvec3(v.eye,self.view.eye) or self.view.eye
v.centre = SceneCamera.syncvec3(v.centre,self.view.centre) or self.view.centre
v.up = SceneCamera.syncvec3(v.up,self.view.up) or self.view.up
v.fov = v.fov or self.view.fov
v.aspect = v.aspect or self.view.aspect
v.size = v.size or self.view.size
v.angle = v.angle or self.view.angle
end
options = options or {}
options.easing = self.options.easing
options.loop = self.options.loop
-- create function to call path
return function(callback,...)
local id = tween.path(time,self.view,path,options,callback,unpack(arg))
return id,SceneCamera.wrap(id)
end
end
-- creates a function to perform a movement of the camera with a single tween
-- when the returned function is called
-- time - the time span of the movement
-- target - the target values to animate from the current position using one or more of the options below
-- e.g {eye={x=5,y=200,z=50},centre={x=10,y=20,z=30},up={x=20,y=50,z=70},fov=35,aspect=600/500}
-- options - any custom options other than the instances default
-- returns a function(callback,...) end which calls a tween.path and returns the id and a table
-- which wraps the id with a stop and reset function.
--if a callback and args is passed its called on completion of the tween
function SceneCamera:move(time,target,options)
if time == nil then return end
-- set defaults or values
local tbl = target or {}
options = options or {}
options.easing = self.options.easing
options.loop = self.options.loop
-- create function which calls tween and returns id
return function(callback,...)
local id = tween(time,self.view
,tbl,options,callback,...)
return id,SceneCamera.wrap(id)
end
end
-- create a function that resets the camera position to the initial values it was created with
function SceneCamera:reset(time)
if time ~= nil and time > 0 then
return function(callback,...)
return self:move(time,self.copyview(self.initial),self.initial.options)(callback,...)
end
else
return function(callback,...)
self.view = SceneCamera.copyview(self.initial)
if callback ~= nil and type(callback) == "function" then callback(...) end
end
end
end
-- wraps tween id within a table which contains a stop and reset function
function SceneCamera.wrap(twid)
return {
id=twid,
stop=function(self) return tween.stop(self.id) end,
reset=function(self) return tween.reset(self.id) end
}
end
-- copy a vec3
function SceneCamera.copyvec3(v)
if v == nil then return end
return vec3(v.x,v.y,v.z)
end
-- sychronise a vec3 with another replacing nils in vx with values from vy
function SceneCamera.syncvec3(vx,vy)
if vx == nil or vy == nil then return end
local x = vx.x or vy.x
local y = vx.y or vy.y
local z = vx.z or vy.z
return vec3(x,y,z)
end
function SceneCamera.copyview(view)
local newView = {}
for i,v in pairs(view) do
if v ~= nil and type(v) == "table" then
newView[i] = SceneCamera.copyview(v)
elseif v ~= nil and type(v) == "userdata" then
newView[i] = SceneCamera.copyvec3(v)
else
newView[i] = v
end
end
return newView
end
-------------------------------------------------------------------------------------------
-- SceneCamera --
-- A library which moves the camera and alters perspective --
-- by animating them with tween, tween.path and tween.sequence --
-- for cinematic effects or any other movement you can think of --
-- Version:1.0.3 --
-- Written by: XanDDemoX --
-------------------------------------------------------------------------------------------
Tower = class()
-- An all in one index based table manipulation (array manipulation) class which takes some inspiration from
-- .net and linq functions.
-- this can also operate, based on usage, as a stack (Last in first out LiFo), queue (First in First out FiFo) or -- Dequeue / (pronounced "deck") which is also called a double ended queue. e.g stack operates normally with push -
-- and pop, queue operates with push and pull. and poke inserts items at the top by default poke is equivalent to
-- table.insert(self._items,1)
-- note: The implementation of dequeue and queue is not as efficient as it could be because it
-- invites the internal table to shift its items to close gaps >_<
-- the user can select when to optimise the different associated functions (push,pop/poke,pull)
-- which inverts the internal table and switches push and pop from inserting and removing from the bottom
-- of the table to the top
-- this also provides powerful manipulation for using the contents of the collection with dynamic functions :D
-- (which is used for calling event callbacks)
function Tower:init(items,indexed)
self._itemCount = 0
self._items = {}
self._optimisePushPop = true
self:addrange(items,indexed)
end
-- internal function to increment / decrement the item count
function Tower:_incCount(amount)
self._itemCount = self._itemCount + amount
end
-- internal property returns the current index to pop items from
function Tower:_popIndex()
if self._optimisePushPop == true then
return self._itemCount
else
return 1
end
end
-- internal property returns the current index to pull items from
function Tower:_pullIndex()
if self._optimisePushPop == false then
return self._itemCount
else
return 1
end
end
-- returns the item at a given index or nil
function Tower:item(index)
if index < 1 or index > self._itemCount then
return nil
else
return self._items[index]
end
end
function Tower:items()
return ipairs(self._items)
end
-- returns a shallow copy of the internal items collection
function Tower:getItems()
local cloneItems = {}
for i,v in ipairs(self._items) do
table.insert(cloneItems,v)
end
return cloneItems
end
-- returns a clone with a shallow copy of the internal items collection
function Tower:clone()
return Tower(self._items)
end
-- inserts an item at the top of the stack (bottom of the table)
function Tower:push(...)
if #arg > 0 then
for i,v in ipairs(arg) do
if self._optimisePushPop == true then
self:insertAt(v)
else
self:insertAt(v,1)
end
end
end
end
function Tower:pushArray(items)
if #items > 0 then
self:push(unpack(items))
end
end
-- removes and returns the first item at the top of the stack / bottom of the queue (bottom of the table)
-- or returns nil
function Tower:pop()
local index = self:_popIndex()
local item = self:item(index)
if item then
self:removeAt(index)
end
return item
end
-- inserts the given item at the the bottom of the stack / top of the queue (top of the table)
function Tower:poke(...)
if #arg > 0 then
for i,v in ipairs(arg) do
if self._optimisePushPop == false then
self:insertAt(v)
else
self:insertAt(v,1)
end
end
end
end
function Tower:pokeArray(items)
if #items > 0 then
self:poke(unpack(items))
end
end
-- removes and returns the first item at the bottom of the stack / top of the queue (top of the table)
function Tower:pull()
local index = self:_pullIndex()
local item = self:item(index)
if item then
self:removeAt(index)
end
return item
end
-- returns the first item at the top of the stack / bottom of the queue (top of the table)
-- or returns nil
function Tower:peek()
return self:item(self:_popIndex())
end
-- returns the first item at the bottom of the stack / top of the queue (bottom of the table)
function Tower:peer()
return self:item(self:_pullIndex())
end
-- swaps push and pop from using the top of the table too the bottom
-- giving cheaper calls when pushing/poping items.
-- but more expensive calls to pull and poke.
function Tower:optimisePushPop()
if self._optimisePushPop == false then
self:invert()
self._optimisePushPop = true
end
end
-- swaps pull and poke from using the top of the table to the bottom
-- giving cheaper calls when pulling / poking items.
-- but more expensive calls to push and pop.
function Tower:optimisePullPoke()
if self._optimisePushPop == true then
self:invert()
self._optimisePushPop = false
end
end
-- example optimise use case: using tower as a large queue
-- tower populated with push initially then call optimisePullPoke
-- this has an initial cost of inverting the table but yeilds cheeper calls to
-- pull because the items are removed from the bottom of the table
-- circomventing the table shifting items up on removal or down on insert
-- (via poke). after using optimisePullPoke calls to push and pop become more
-- expensive as they are switched to use the top of the table instead
-- returns the count of items currently in the internal collection
function Tower:count()
return self._itemCount
end
function Tower:invert()
if self._itemCount > 0 then
local items = self:getItems()
local count = self._itemCount
self:clear()
-- counter for decrementing index
local c = count
for i=1, count do
self:insertAt(items[c])
c = c - 1
end
end
end
-- clears all items from the tower
function Tower:clear()
self._items = {}
self._itemCount = 0
end
-- returns the first index from the top within the internal collection of the given item
-- otherwise returns -1
function Tower:indexOf(item)
local items = self._items
for i,v in ipairs(items) do
if v == item then return i end
end
return -1
end
-- inserts the given item at the given index or the bottom of the collection (if index is > count)
-- returns the true if the item was inserted or false if the index is < 0
-- if index is nil item is inserted at the bottom of the collection
function Tower:insertAt(item, index)
if item == nil then return end
if index then
if index > self._itemCount then
table.insert(self._items,item)
elseif index < 0 then
table.insert(self._items,1,item)
else
table.insert(self._items,index,item)
end
else
table.insert(self._items,item)
end
self:_incCount(1)
return true
end
-- updates the item at the given index with the given new value
-- returns true if the item was updated or false if the index is out of range
function Tower:updateAt(index,newValue)
if index < 0 or index > self._itemCount then
return false
else
self._items[index] = newValue
return true
end
end
-- returns true if the item at the given index was removed from the internal collection
-- otherwise returns false
function Tower:removeAt(index)
if index < 0 or index > self._itemCount then
return false
else
table.remove(self._items,index)
self:_incCount(-1)
return true
end
end
-- returns true if the given item is contained within the internal collection
-- otherwise returns false
function Tower:contains(item)
for i,v in ipairs(self._items) do
if v == item then return true end
end
return false
end
function Tower:addrange(items,indexed)
if items == nil then return end
if #items > 0 then
if indexed == nil then indexed = true end
if indexed then
for i,v in ipairs(items) do
self:insertAt(v)
end
else
for i,v in pairs(items) do
self:insertAt(v)
end
end
end
end
function Tower:add(item)
self:insertAt(item)
end
-- returns true if the given item was removed from the internal collection
-- otherwise returns false
function Tower:remove(item)
local index = self:indexOf(item)
if index > 0 then
return self:removeAt(index)
end
return false
end
-- returns the first item matched by the given function(item) from the top of the internal collection
-- otherwise returns nil
function Tower:find(findDelegate)
local items = self._items
for i,v in ipairs(items) do
if findDelegate(v) then return v end
end
return nil
end
-- returns the first index from the top of the item matched by the given function(item)
-- otherwise returns -1
function Tower:findIndex(findDelegate)
local items = self._items
for i,v in ipairs(items) do
if findDelegate(v) == true then return i end
end
return -1
end
-- returns all indexs matched by the given function(item)
-- otherwise returns nil
function Tower:findIndexes(findDelegate)
local items = self._items
local foundItems = {}
for i,v in ipairs(items) do
if findDelegate(v) then
table.insert(foundItems,i)
end
end
return foundItems
end
-- returns all items matched by the given function(item)
-- otherwise returns nil
function Tower:findAll(findDelegate)
local items = self._items
local foundItems = {}
for i,v in ipairs(items) do
if findDelegate(v) then
table.insert(foundItems,v)
end
end
return foundItems
end
-- updates the first item from the top of the colection
-- which is matched by the given function(item) with the given new value
-- returns true if an item was updated otherwise false
function Tower:update(findDelegate,newValue)
local index = self:findIndex(findDelegate)
if index > 0 then
return self:updateAt(index,newValue)
end
return false
end
-- updates all items that match the given function(item) with the given new value
-- returns the count of items updated
function Tower:updateAll(findDelegate,newValue)
local indexes = self:findIndexes(findDelegate)
local count = 0
for i,v in ipairs(indexes) do
if self:updateAt(index,newValue) then count = count + 1 end
end
return count
end
-- removes the first item from the top of the collection which is matched by the given function(item)
function Tower:delete(findDelegate)
local item = self:find(findDelegate)
if item then
return self:remove(item)
end
return false
end
-- removes the all items from the collection which are matched by the given function(item)
-- returns the count of items removed
function Tower:deleteAll(findDelegate)
local indexes = self:findIndexes(findDelegate)
local count = 0
for i,v in ipairs(indexes) do
if self:removeAt((v-count)) then count = count + 1 end
end
return count
end
-- maps the items contained in the internal collection to the given function
function Tower:paramMap(func)
if func == nil then return function() end end
if self._itemCount < 1 then return function() end end
local funcType = type(func)
if funcType == "function" then
local items = self:getItems()
return function () return func(unpack(items)) end
elseif funcType == "table" then
local items = self:clone()
local functable = Tower(func)
return function()
local result = Tower()
functable:foreach(function(f) if f then result:add(items:paramMap(f)()) end end)
return result
end
else
return function() end
end
end
-- maps an item to the parameter of a given function for each item contained in the within the internal collection
function Tower:map(func)
if self._itemCount < 1 then return nil end
local funcs = {}
local items = self:getItems()
for i,v in ipairs(items) do
funcs[i] = func(v)
end
return funcs
end
-- maps an item as the parameters to a given function for each item within the internal collection
function Tower:mapn(func)
if self._itemCount < 1 then return nil end
local result = {}
local items = self:getItems()
local count = self._itemCount
local n = #self:peer()[1]
for i=1,n do
local args = {}
for k,v in ipairs(items) do
table.insert(args,v[i])
end
local f = func(unpack(args))
table.insert(result,f)
end
return result
end
-- iterates the internal collection calling a user defined function
function Tower:foreach(func)
if func then
for k,v in self:items() do
func(v)
end
end
end
-- sorts a (shallow) cloned insance of self and returns
function Tower:clonesort(func)
if func then
local cloneobj = self:clone()
cloneobj:sort(func)
return cloneobj
else
return nil
end
end
-- sorts the internal collection with a user defined function using table.sort
function Tower:sort(func)
if func then
table.sort(self._items,func)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment