spine-runtimes/spine-lua/Animation.lua
NathanSweet c0fdc454a2 Fixed inconsistent newlines.
License header was followed by \n\n and files ended with \n. Changed to \r\n.
2016-10-15 23:45:16 +02:00

924 lines
31 KiB
Lua

-------------------------------------------------------------------------------
-- Spine Runtimes Software License v2.5
--
-- Copyright (c) 2013-2016, Esoteric Software
-- All rights reserved.
--
-- You are granted a perpetual, non-exclusive, non-sublicensable, and
-- non-transferable license to use, install, execute, and perform the Spine
-- Runtimes software and derivative works solely for personal or internal
-- use. Without the written permission of Esoteric Software (see Section 2 of
-- the Spine Software License Agreement), you may not (a) modify, translate,
-- adapt, or develop new applications using the Spine Runtimes or otherwise
-- create derivative works or improvements of the Spine Runtimes or (b) remove,
-- delete, alter, or obscure any trademarks or any copyright, trademark, patent,
-- or other intellectual property or proprietary rights notices on or in the
-- Software, including any copy thereof. Redistributions in binary or source
-- form must include this license and terms.
--
-- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
-- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-- EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
-- USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-- IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
-- FIXME
-- All the indexing in this file is zero based. We use zlen()
-- instead of the # operator. Initialization of number arrays
-- is performed via utils.newNumberArrayZero. This needs
-- to be rewritten using one-based indexing for better performance
local utils = require "spine-lua.utils"
local AttachmentType = require "spine-lua.attachments.AttachmentType"
local function zlen(array)
return #array + 1
end
local Animation = {}
function Animation.new (name, timelines, duration)
if not timelines then error("timelines cannot be nil", 2) end
local self = {
name = name,
timelines = timelines,
duration = duration
}
function self:apply (skeleton, lastTime, time, loop, events)
if not skeleton then error("skeleton cannot be nil.", 2) end
if loop and duration > 0 then
time = time % self.duration
if lastTime > 0 then lastTime = lastTime % self.duration end
end
for i,timeline in ipairs(self.timelines) do
timeline:apply(skeleton, lastTime, time, events, 1)
end
end
function self:mix (skeleton, lastTime, time, loop, events, alpha)
if not skeleton then error("skeleton cannot be nil.", 2) end
if loop and duration > 0 then
time = time % self.duration
if lastTime > 0 then lastTime = lastTime % self.duration end
end
for i,timeline in ipairs(self.timelines) do
timeline:apply(skeleton, lastTime, time, events, alpha)
end
end
return self
end
local function binarySearch (values, target, step)
local low = 0
local high = math.floor(zlen(values) / step - 2)
if high == 0 then return step end
local current = math.floor(high / 2)
while true do
if values[(current + 1) * step] <= target then
low = current + 1
else
high = current
end
if low == high then return (low + 1) * step end
current = math.floor((low + high) / 2)
end
end
local function binarySearch1 (values, target)
local low = 0
local high = math.floor(zlen(values) - 2)
if high == 0 then return 1 end
local current = math.floor(high / 2)
while true do
if values[current + 1] <= target then
low = current + 1
else
high = current
end
if low == high then return low + 1 end
current = math.floor((low + high) / 2)
end
end
local function linearSearch (values, target, step)
local i = 0
local last = zlen(values) - step
while i <= last do
if (values[i] > target) then return i end
i = i + step
end
return -1
end
Animation.CurveTimeline = {}
function Animation.CurveTimeline.new (frameCount)
local LINEAR = 0
local STEPPED = 1
local BEZIER = 2
local BEZIER_SIZE = 10 * 2 - 1
local self = {
curves = utils.newNumberArrayZero((frameCount - 1) * BEZIER_SIZE) -- type, x, y, ...
}
function self:getFrameCount ()
return math.floor(zlen(self.curves) / BEZIER_SIZE) + 1
end
function self:setStepped (frameIndex)
self.curves[frameIndex * BEZIER_SIZE] = STEPPED
end
function self:getCurveType (frameIndex)
local index = frameIndex * BEZIER_SIZE
if index == zlen(self.curves) then return LINEAR end
local type = self.curves[index]
if type == LINEAR then return LINEAR end
if type == STEPPED then return STEPPED end
return BEZIER
end
function self:setCurve (frameIndex, cx1, cy1, cx2, cy2)
local tmpx = (-cx1 * 2 + cx2) * 0.03
local tmpy = (-cy1 * 2 + cy2) * 0.03
local dddfx = ((cx1 - cx2) * 3 + 1) * 0.006
local dddfy = ((cy1 - cy2) * 3 + 1) * 0.006
local ddfx = tmpx * 2 + dddfx
local ddfy = tmpy * 2 + dddfy
local dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667
local dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667
local i = frameIndex * BEZIER_SIZE
local curves = self.curves
curves[i] = BEZIER
i = i + 1
local x = dfx
local y = dfy
local n = i + BEZIER_SIZE - 1
while i < n do
curves[i] = x
curves[i + 1] = y
dfx = dfx + ddfx
dfy = dfy + ddfy
ddfx = ddfx + dddfx
ddfy = ddfy + dddfy
x = x + dfx
y = y + dfy
i = i + 2
end
end
function self:getCurvePercent (frameIndex, percent)
percent = utils.clamp(percent, 0, 1)
local curves = self.curves
local i = frameIndex * BEZIER_SIZE
local type = curves[i]
if type == LINEAR then return percent end
if type == STEPPED then return 0 end
i = i + 1
local x
local n = i + BEZIER_SIZE - 1
local start = i
while i < n do
x = curves[i]
if x >= percent then
local prevX, prevY
if i == start then
prevX = 0
prevY = 0
else
prevX = curves[i - 2]
prevY = curves[i - 1]
end
return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX)
end
i = i + 2
end
local y = curves[i - 1]
return y + (1 - y) * (percent - x) / (1 - x) -- Last point is 1,1.
end
return self
end
Animation.RotateTimeline = {}
Animation.RotateTimeline.ENTRIES = 2
function Animation.RotateTimeline.new (frameCount)
local ENTRIES = Animation.RotateTimeline.ENTRIES
local PREV_TIME = -2
local PREV_ROTATION = -1
local ROTATION = 1
local self = Animation.CurveTimeline.new(frameCount)
self.boneIndex = -1
self.frames = utils.newNumberArrayZero(frameCount * 2)
function self:setFrame (frameIndex, time, degrees)
frameIndex = frameIndex * 2
self.frames[frameIndex] = time
self.frames[frameIndex + ROTATION] = degrees
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local bone = skeleton.bones[self.boneIndex]
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
local amount = bone.data.rotation + frames[zlen(frames) + PREV_ROTATION] - bone.rotation
while amount > 180 do
amount = amount - 360
end
while amount < -180 do
amount = amount + 360
end
bone.rotation = bone.rotation + amount * alpha
return
end
-- Interpolate between the last frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
local prevRotation = frames[frame + PREV_ROTATION]
local frameTime = frames[frame]
local percent = self:getCurvePercent((math.floor(frame / 2)) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
local amount = frames[frame + ROTATION] - prevRotation
while amount > 180 do
amount = amount - 360
end
while amount < -180 do
amount = amount + 360
end
amount = bone.data.rotation + (prevRotation + amount * percent) - bone.rotation
while amount > 180 do
amount = amount - 360
end
while amount < -180 do
amount = amount + 360
end
bone.rotation = bone.rotation + amount * alpha
end
return self
end
Animation.TranslateTimeline = {}
Animation.TranslateTimeline.ENTRIES = 3
function Animation.TranslateTimeline.new (frameCount)
local ENTRIES = Animation.TranslateTimeline.ENTRIES
local PREV_TIME = -3
local PREV_X = -2
local PREV_Y = -1
local X = 1
local Y = 2
local self = Animation.CurveTimeline.new(frameCount)
self.frames = utils.newNumberArrayZero(frameCount * ENTRIES)
self.boneIndex = -1
function self:setFrame (frameIndex, time, x, y)
frameIndex = frameIndex * ENTRIES
self.frames[frameIndex] = time
self.frames[frameIndex + X] = x
self.frames[frameIndex + Y] = y
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local bone = skeleton.bones[self.boneIndex]
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
bone.x = bone.x + (bone.data.x + frames[zlen(frames) + PREV_X] - bone.x) * alpha
bone.y = bone.y + (bone.data.y + frames[zlen(frames) + PREV_Y] - bone.y) * alpha
return
end
-- Interpolate between the last frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
local prevX = frames[frame + PREV_X]
local prevY = frames[frame + PREV_Y]
local frameTime = frames[frame]
local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
bone.x = bone.x + (bone.data.x + prevX + (frames[frame + X] - prevX) * percent - bone.x) * alpha
bone.y = bone.y + (bone.data.y + prevY + (frames[frame + Y] - prevY) * percent - bone.y) * alpha
end
return self
end
Animation.ScaleTimeline = {}
Animation.ScaleTimeline.ENTRIES = Animation.TranslateTimeline.ENTRIES
function Animation.ScaleTimeline.new (frameCount)
local ENTRIES = Animation.ScaleTimeline.ENTRIES
local PREV_TIME = -3
local PREV_X = -2
local PREV_Y = -1
local X = 1
local Y = 2
local self = Animation.TranslateTimeline.new(frameCount)
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local bone = skeleton.bones[self.boneIndex]
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
bone.scaleX = bone.scaleX + (bone.data.scaleX * frames[zlen(frames) + PREV_X] - bone.scaleX) * alpha
bone.scaleY = bone.scaleY + (bone.data.scaleY * frames[zlen(frames) + PREV_Y] - bone.scaleY) * alpha
return
end
-- Interpolate between the last frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
local prevX = frames[frame + PREV_X]
local prevY = frames[frame + PREV_Y]
local frameTime = frames[frame]
local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1,
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
bone.scaleX = bone.scaleX + (bone.data.scaleX * (prevX + (frames[frame + X] - prevX) * percent) - bone.scaleX) * alpha
bone.scaleY = bone.scaleY + (bone.data.scaleY * (prevY + (frames[frame + Y] - prevY) * percent) - bone.scaleY) * alpha
end
return self
end
Animation.ShearTimeline = {}
Animation.ShearTimeline.ENTRIES = Animation.TranslateTimeline.ENTRIES
function Animation.ShearTimeline.new (frameCount)
local ENTRIES = Animation.ShearTimeline.ENTRIES
local PREV_TIME = -3
local PREV_X = -2
local PREV_Y = -1
local X = 1
local Y = 2
local self = Animation.TranslateTimeline.new(frameCount)
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local bone = skeleton.bones[self.boneIndex]
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
bone.shearX = bone.shearX + (bone.data.shearX * frames[zlen(frames) + PREV_X] - bone.shearX) * alpha
bone.shearY = bone.shearY + (bone.data.shearY * frames[zlen(frames) + PREV_Y] - bone.shearY) * alpha
return
end
-- Interpolate between the last frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
local prevX = frames[frame + PREV_X]
local prevY = frames[frame + PREV_Y]
local frameTime = frames[frame]
local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1,
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
bone.shearX = bone.shearX + (bone.data.shearX + (prevX + (frames[frame + X] - prevX) * percent) - bone.shearX) * alpha
bone.shearY = bone.shearY + (bone.data.shearY + (prevY + (frames[frame + Y] - prevY) * percent) - bone.shearY) * alpha
end
return self
end
Animation.ColorTimeline = {}
Animation.ColorTimeline.ENTRIES = 5
function Animation.ColorTimeline.new (frameCount)
local ENTRIES = Animation.ColorTimeline.ENTRIES
local PREV_TIME = -5
local PREV_R = -4
local PREV_G = -3
local PREV_B = -2
local PREV_A = -1
local R = 1
local G = 2
local B = 3
local A = 4
local self = Animation.CurveTimeline.new(frameCount)
self.frames = utils.newNumberArrayZero(frameCount * ENTRIES)
self.slotIndex = -1
function self:setFrame (frameIndex, time, r, g, b, a)
frameIndex = frameIndex * ENTRIES
self.frames[frameIndex] = time
self.frames[frameIndex + R] = r
self.frames[frameIndex + G] = g
self.frames[frameIndex + B] = b
self.frames[frameIndex + A] = a
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local r, g, b, a
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
local i = zlen(frames)
r = frames[i + PREV_R]
g = frames[i + PREV_G]
b = frames[i + PREV_B]
a = frames[i + PREV_A]
else
-- Interpolate between the last frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
r = frames[frame + PREV_R]
g = frames[frame + PREV_G]
b = frames[frame + PREV_B]
a = frames[frame + PREV_A]
local frameTime = frames[frame]
local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1,
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
r = r + (frames[frame + R] - r) * percent
g = g + (frames[frame + G] - g) * percent
b = b + (frames[frame + B] - b) * percent
a = a + (frames[frame + A] - a) * percent
end
local color = skeleton.slots[self.slotIndex].color
if alpha < 1 then
color:add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha)
else
color:set(r, g, b, a)
end
end
return self
end
Animation.AttachmentTimeline = {}
function Animation.AttachmentTimeline.new (frameCount)
local self = {
frames = utils.newNumberArrayZero(frameCount), -- time, ...
attachmentNames = {},
slotName = nil
}
function self:getFrameCount ()
return zlen(self.frames)
end
function self:setFrame (frameIndex, time, attachmentName)
self.frames[frameIndex] = time
self.attachmentNames[frameIndex] = attachmentName
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end
local frameIndex = 0
if time >= frames[zlen(frames) - 1] then
frameIndex = zlen(frames) - 1
else
frameIndex = binarySearch(frames, time, 1) - 1
end
local attachmentName = self.attachmentNames[frameIndex]
local slot = skeleton.slotsByName[self.slotName]
if attachmentName then
if not slot.attachment then
slot:setAttachment(skeleton:getAttachment(self.slotName, attachmentName))
elseif slot.attachment.name ~= attachmentName then
slot:setAttachment(skeleton:getAttachment(self.slotName, attachmentName))
end
else
slot:setAttachment(nil)
end
end
return self
end
Animation.EventTimeline = {}
function Animation.EventTimeline.new (frameCount)
local self = {
frames = utils.newNumberArrayZero(frameCount),
events = {}
}
function self:getFrameCount ()
return zlen(self.frames)
end
function self:setFrame (frameIndex, event)
self.frames[frameIndex] = event.time
self.events[frameIndex] = event
end
-- Fires events for frames > lastTime and <= time.
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
if not firedEvents then return end
local frames = self.frames
local frameCount = zlen(frames)
if lastTime > time then -- Fire events after last time for looped animations.
self:apply(skeleton, lastTime, 999999, firedEvents, alpha)
lastTime = -1
elseif lastTime >= frames[frameCount - 1] then -- Last time is after last frame.
return
end
if time < frames[0] then return end -- Time is before first frame.
local frame
if lastTime < frames[0] then
frame = 0
else
frame = binarySearch1(frames, lastTime)
local frame = frames[frame]
while frame > 0 do -- Fire multiple events with the same frame.
if frames[frame - 1] ~= frame then break end
frame = frame - 1
end
end
local events = self.events
while frame < frameCount and time >= frames[frame] do
table.insert(firedEvents, events[frame])
frame = frame + 1
end
end
return self
end
Animation.DrawOrderTimeline = {}
function Animation.DrawOrderTimeline.new (frameCount)
local self = {
frames = utils.newNumberArrayZero(frameCount),
drawOrders = {}
}
function self:getFrameCount ()
return zlen(self.frames)
end
function self:setFrame (frameIndex, time, drawOrder)
self.frames[frameIndex] = time
self.drawOrders[frameIndex] = drawOrder
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local frame
if time >= frames[zlen(frames) - 1] then -- Time is after last frame.
frame = zlen(frames) - 1
else
frame = binarySearch1(frames, time) - 1
end
local drawOrder = skeleton.drawOrder
local slots = skeleton.slots
local drawOrderToSetupIndex = self.drawOrders[frame]
if not drawOrderToSetupIndex then
for i,slot in ipairs(slots) do
drawOrder[i] = slots[i]
end
else
for i,setupIndex in ipairs(drawOrderToSetupIndex) do
drawOrder[i] = skeleton.slots[setupIndex]
end
end
end
return self
end
Animation.DeformTimeline = {}
function Animation.DeformTimeline.new (frameCount)
local self = Animation.CurveTimeline.new(frameCount)
self.frames = utils.newNumberArrayZero(frameCount)
self.frameVertices = utils.newNumberArrayZero(frameCount)
self.slotIndex = -1
self.attachment = nil
function self:setFrame (frameIndex, time, vertices)
self.frames[frameIndex] = time
self.frameVertices[frameIndex] = vertices
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local slot = skeleton.slots[self.slotIndex]
local slotAttachment = slot.attachment
if not slotAttachment then return end
if not (slotAttachment.type == AttachmentType.mesh or slotAttachment.type == AttachmentType.linkedmesh or slotAttachment.type == AttachmentType.path) then return end
if not slotAttachment:applyDeform(self.attachment) then return end
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local frameVertices = self.frameVertices
local vertexCount = #(frameVertices[0])
local verticesArray = slot.attachmentVertices
if (#verticesArray ~= vertexCount) then alpha = 1 end -- Don't mix from uninitialized slot vertices.
local vertices = utils.setArraySize(verticesArray, vertexCount)
if time >= frames[zlen(frames) - 1] then
local lastVertices = frameVertices[zlen(frames) - 1]
if alpha < 1 then
local i = 1
while i <= vertexCount do
vertices[i] = vertices[i] + (lastVertices[i] - vertices[i]) * alpha
i = i + 1
end
else
local i = 1
while i <= vertexCount do
vertices[i] = lastVertices[i]
i = i + 1
end
end
return;
end
-- Interpolate between the previous frame and the current frame.
local frame = binarySearch(frames, time, 1)
local prevVertices = frameVertices[frame - 1]
local nextVertices = frameVertices[frame]
local frameTime = frames[frame]
local percent = self:getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime))
if alpha < 1 then
local i = 1
while i <= vertexCount do
local prev = prevVertices[i]
vertices[i] = vertices[i] + (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha
i = i + 1
end
else
local i = 1
while i <= vertexCount do
local prev = prevVertices[i]
vertices[i] = prev + (nextVertices[i] - prev) * percent
i = i + 1
end
end
end
return self
end
Animation.IkConstraintTimeline = {}
Animation.IkConstraintTimeline.ENTRIES = 3
function Animation.IkConstraintTimeline.new (frameCount)
local ENTRIES = Animation.IkConstraintTimeline.ENTRIES
local PREV_TIME = -3
local PREV_MIX = -2
local PREV_BEND_DIRECTION = -1
local MIX = 1
local BEND_DIRECTION = 2
local self = Animation.CurveTimeline.new(frameCount)
self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) -- time, mix, bendDirection, ...
self.ikConstraintIndex = -1
function self:setFrame (frameIndex, time, mix, bendDirection)
frameIndex = frameIndex * ENTRIES
self.frames[frameIndex] = time
self.frames[frameIndex + MIX] = mix
self.frames[frameIndex + BEND_DIRECTION] = bendDirection
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local constraint = skeleton.ikConstraints[self.ikConstraintIndex]
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
constraint.mix = constraint.mix + (frames[zlen(frames) + PREV_MIX] - constraint.mix) * alpha
constraint.bendDirection = frames[zlen(frames) + PREV_BEND_DIRECTION]
return
end
-- Interpolate between the previous frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
local mix = frames[frame + PREV_MIX]
local frameTime = frames[frame]
local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1,
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
constraint.mix = constraint.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha
constraint.bendDirection = math.floor(frames[frame + PREV_BEND_DIRECTION])
end
return self
end
Animation.TransformConstraintTimeline = {}
Animation.TransformConstraintTimeline.ENTRIES = 5
function Animation.TransformConstraintTimeline.new (frameCount)
local ENTRIES = Animation.TransformConstraintTimeline.ENTRIES
local PREV_TIME = -5
local PREV_ROTATE = -4
local PREV_TRANSLATE = -3
local PREV_SCALE = -2
local PREV_SHEAR = -1
local ROTATE = 1
local TRANSLATE = 2
local SCALE = 3
local SHEAR = 4
local self = Animation.CurveTimeline.new(frameCount)
self.frames = utils.newNumberArrayZero(frameCount * ENTRIES)
self.transformConstraintIndex = -1
function self:setFrame (frameIndex, time, rotateMix, translateMix, scaleMix, shearMix)
frameIndex = frameIndex * ENTRIES
self.frames[frameIndex] = time
self.frames[frameIndex + ROTATE] = rotateMix
self.frames[frameIndex + TRANSLATE] = translateMix
self.frames[frameIndex + SCALE] = scaleMix
self.frames[frameIndex + SHEAR] = shearMix
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if time < frames[0] then return end -- Time is before first frame.
local constraint = skeleton.transformConstraints[self.transformConstraintIndex]
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
local i = zlen(frames)
constraint.rotateMix = constraintMix.rotateMix + (frames[i + PREV_ROTATE] - constraint.rotateMix) * alpha
constraint.translateMix = constraintMix.translateMix + (frames[i + PREV_TRANSLATE] - constraint.translateMix) * alpha
constraint.scaleMix = constraintMix.scaleMix + (frames[i + PREV_SCALE] - constraint.scaleMix) * alpha
constraint.shearMix = constraintMix.shearMix + (frames[i + PREV_SHEAR] - constraint.shearMix) * alpha
return
end
-- Interpolate between the last frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
local frameTime = frames[frame]
local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
local rotate = frames[frame + PREV_ROTATE]
local translate = frames[frame + PREV_TRANSLATE]
local scale = frames[frame + PREV_SCALE]
local shear = frames[frame + PREV_SHEAR]
constraint.rotateMix = constraint.rotateMix + (rotate + (frames[frame + ROTATE] - rotate) * percent - constraint.rotateMix) * alpha
constraint.translateMix = constraint.translateMix + (translate + (frames[frame + TRANSLATE] - translate) * percent - constraint.translateMix) * alpha
constraint.scaleMix = constraint.scaleMix + (scale + (frames[frame + SCALE] - scale) * percent - constraint.scaleMix) * alpha
constraint.shearMix = constraint.shearMix + (shear + (frames[frame + SHEAR] - shear) * percent - constraint.shearMix) * alpha
end
return self
end
Animation.PathConstraintPositionTimeline = {}
Animation.PathConstraintPositionTimeline.ENTRIES = 2
function Animation.PathConstraintPositionTimeline.new (frameCount)
local ENTRIES = Animation.PathConstraintPositionTimeline.ENTRIES
local PREV_TIME = -2
local PREV_VALUE = -1
local VALUE = 1
local self = Animation.CurveTimeline.new(frameCount)
self.frames = utils.newNumberArrayZero(frameCount * ENTRIES)
self.pathConstraintIndex = -1
function self:setFrame (frameIndex, time, value)
frameIndex = frameIndex * ENTRIES
self.frames[frameIndex] = time
self.frames[frameIndex + VALUE] = value
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if (time < frames[0]) then return end -- Time is before first frame.
local constraint = skeleton.pathConstraints[self.pathConstraintIndex]
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
local i = zlen(frames)
constraint.position = constraint.position + (frames[i + PREV_VALUE] - constraint.position) * alpha
return
end
-- Interpolate between the previous frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
local position = frames[frame + PREV_VALUE]
local frameTime = frames[frame]
local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
constraint.position = constraint.position + (position + (frames[frame + VALUE] - position) * percent - constraint.position) * alpha
end
return self
end
Animation.PathConstraintSpacingTimeline = {}
Animation.PathConstraintSpacingTimeline.ENTRIES = 2
function Animation.PathConstraintSpacingTimeline.new (frameCount)
local ENTRIES = Animation.PathConstraintSpacingTimeline.ENTRIES
local PREV_TIME = -2
local PREV_VALUE = -1
local VALUE = 1
local self = Animation.CurveTimeline.new(frameCount)
self.frames = utils.newNumberArrayZero(frameCount * ENTRIES)
self.pathConstraintIndex = -1
function self:setFrame (frameIndex, time, value)
frameIndex = frameIndex * ENTRIES
self.frames[frameIndex] = time
self.frames[frameIndex + VALUE] = value
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if (time < frames[0]) then return end -- Time is before first frame.
local constraint = skeleton.pathConstraints[self.pathConstraintIndex]
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
local i = zlen(frames)
constraint.spacing = constraint.spacing + (frames[i + PREV_VALUE] - constraint.spacing) * alpha
return
end
-- Interpolate between the previous frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
local spacing = frames[frame + PREV_VALUE]
local frameTime = frames[frame]
local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
constraint.spacing = constraint.spacing + (spacing + (frames[frame + VALUE] - spacing) * percent - constraint.spacing) * alpha
end
return self
end
Animation.PathConstraintMixTimeline = {}
Animation.PathConstraintMixTimeline.ENTRIES = 3
function Animation.PathConstraintMixTimeline.new (frameCount)
local ENTRIES = Animation.PathConstraintMixTimeline.ENTRIES
local PREV_TIME = -3
local PREV_ROTATE = -2
local PREV_TRANSLATE = -1
local ROTATE = 1
local TRANSLATE = 2
local self = Animation.CurveTimeline.new(frameCount)
self.frames = utils.newNumberArrayZero(frameCount * ENTRIES)
self.pathConstraintIndex = -1
function self:setFrame (frameIndex, time, rotateMix, translateMix)
frameIndex = frameIndex * ENTRIES
self.frames[frameIndex] = time
self.frames[frameIndex + ROTATE] = rotateMix
self.frames[frameIndex + TRANSLATE] = translateMix
end
function self:apply (skeleton, lastTime, time, firedEvents, alpha)
local frames = self.frames
if (time < frames[0]) then return end -- Time is before first frame.
local constraint = skeleton.pathConstraints[self.pathConstraintIndex]
if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
local i = zlen(frames)
constraint.rotateMix = constraint.rotateMix + (frames[i + PREV_ROTATE] - constraint.rotateMix) * alpha
constraint.translateMix = constraint.translateMix + (frames[i + PREV_TRANSLATE] - constraint.translateMix) * alpha
return
end
-- Interpolate between the previous frame and the current frame.
local frame = binarySearch(frames, time, ENTRIES)
local rotate = frames[frame + PREV_ROTATE]
local translate = frames[frame + PREV_TRANSLATE]
local frameTime = frames[frame]
local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
constraint.rotateMix = constraint.rotateMix + (rotate + (frames[frame + ROTATE] - rotate) * percent - constraint.rotateMix) * alpha
constraint.translateMix = constraint.translateMix + (translate + (frames[frame + TRANSLATE] - translate) * percent - constraint.translateMix) * alpha
end
return self
end
return Animation