create new empty continuous layer with linked cels - nothing less, nothing more.
i couldn’t find the way how to link cels in new layer, so here’s nightmarish script abomination with workaround:
the script creates new temporary sprite, then creates new empty 1x1 image, draws a pixel to create new cel, replaces that cel’s image with empty image, adds as many frames as needed, copies the layer, pastes it to the original sprite, renames the new layer and silently closes temporary sprite.
if you know how to do this more elegantly, please let me know.
WARNING: since the script contains sprite.close()
command, be sure to save your work first!
app.transaction(
function()
local image = Image(1, 1)
local sprite = app.activeSprite
local colorMode = sprite.colorMode
local frame = app.activeFrame
local layer = app.activeLayer
local numba = #sprite.layers
local nuSprite = Sprite(sprite.width, sprite.height, colorMode)
local nuLayer = nuSprite.layers[1]
nuLayer.isContinuous = true
app.command.GotoFirstFrame()
app.useTool{
tool="filled_rectangle",
points={ Point(0, 0) },
button=MouseButton.LEFT
}
local cel = app.activeCel
cel.image = image
for i = 1, #sprite.frames-1 do
app.command.NewFrame()
end
app.range.layers = { nuLayer }
app.command.Copy()
app.activeSprite = sprite
app.activeLayer = layer
app.command.Paste()
app.activeFrame = frame
app.activeLayer = sprite.layers[numba+1]
nnLayer = app.activeLayer
nnLayer.name = "Layer " .. numba + 1
nuSprite:close()
end)
TODO: create new layer above current layer, not at the top of the stack
1 Like
I salute your efforts, linking cels is one of those things that seems easy but there’s no clear explanation anywhere on how to do it.
The trick that I found is that in order to link cels, or even select them to be in the active range you need to specify BOTH - layers and frames, cels are on their intersection. With this knowledge you can write a script like this:
app.transaction(function()
local sprite = app.activeSprite
-- Create a new layer
local newLayer = sprite:newLayer()
newLayer.isContinuous = true
-- Creat a cel in the first frame of the layer, without this cels won't link
sprite:newCel(newLayer, 1)
-- Get all of the frame numbers into a table/array
local frames = {}
for frameNumber, _ in ipairs(sprite.frames) do
table.insert(frames, frameNumber)
end
-- In order to select cels in range we need to specify both layers and frame - cels are their intersection
app.range.frames = frames
app.range.layers = {newLayer}
app.command.LinkCels()
app.range:clear()
end)
No need for a new sprite in this approach which means no risk of losing data.
Definitely, you should add the feature where the new layer is above the current one and not on top of the stack - it will mimic how native Aseprite functionality for adding layers behaves, making it more intuitive for users. I’d suggest looking into Layer.stackIndex to achieve that.
3 Likes
amazing! thank you very much, this is most helpful.
1 Like
added couple of lines to add new layer above current active layer + name new layer as "Layer " + number:
app.transaction(function()
local sprite = app.activeSprite
local index = app.activeLayer.stackIndex
local stack = #sprite.layers
-- Create a new layer
local newLayer = sprite:newLayer()
newLayer.isContinuous = true
-- Creat a cel in the first frame of the layer, without this cels won't link
sprite:newCel(newLayer, 1)
-- Get all of the frame numbers into a table/array
local frames = {}
for frameNumber, _ in ipairs(sprite.frames) do
table.insert(frames, frameNumber)
end
-- In order to select cels in range we need to specify both layers and frame - cels are their intersection
app.range.frames = frames
app.range.layers = {newLayer}
app.command.LinkCels()
app.range:clear()
newLayer.stackIndex = index + 1
newLayer.name = "Layer " .. stack + 1
end)
2 Likes
Hello,
A few things if you want to keep working on this. Stack indices are relative to a layer’s parent, not the sprite.
A layer’s parent may either be another layer or the sprite.
A sprite’s layers array refers to its immediate children.
So maybe something like this if you want the new layer to appear above the old, but to share a parent.
local function countChildLayers(layer, sum)
if layer.isGroup then
local subLayers = layer.layers
local localSum = 0
for i = 1, #subLayers, 1 do
local subLayer = subLayers[i]
localSum = countChildLayers(subLayer, localSum)
end
return sum + 1 + localSum
else
return sum + 1
end
end
local function countSpriteTotalLayers(sprite)
local layers = sprite.layers
local sum = 0
for i = 1, #layers, 1 do
local layer = layers[i]
sum = countChildLayers(layer, sum)
end
return sum
end
app.transaction(function()
local sprite = app.activeSprite
local actLayer = app.activeLayer
local index = actLayer.stackIndex
local parent = actLayer.parent
-- Create a new layer
local newLayer = sprite:newLayer()
newLayer.isContinuous = true
-- Creat a cel in the first frame of the layer, without this cels won't link
sprite:newCel(newLayer, 1)
-- Get all of the frame numbers into a table/array
local frames = {}
for frameNumber, _ in ipairs(sprite.frames) do
table.insert(frames, frameNumber)
end
-- In order to select cels in range we need to specify both layers and frame - cels are their intersection
app.range.frames = frames
app.range.layers = {newLayer}
app.command.LinkCels()
app.range:clear()
newLayer.parent = parent
newLayer.stackIndex = index + 1
newLayer.name = "Layer " .. countSpriteTotalLayers(sprite)
end)
Alternatively, you may want the new layer to appear at the top of the sprite stack no matter the active layer’s parent. Or you may not want a group layer to count towards the total sum of layers in a sprite.
A side complaint: The setter and getter for app.range.frames
are weird. Looks like the setter takes frame numbers while the getter returns frame objects. I realize that at some point there was a change in how frames were handled elsewhere, so maybe this was overlooked? The documentation could be updated or the underlying API could be redesigned. If there can’t be reciprocity between a getter and a setter, then there should be separate methods and not a property.
Best,
Jeremy
2 Likes
i didn’t think about groups at all, so thanks for this improvement!
i don’t know much about ranges, so what i find confusing is that app.range.cels
can be used to get range only. that threw me off right from the start.
@behreandtjeremy That’s a great point, I also didn’t even think about groups!
I’d suggest a recursive implementation that doesn’t count the actual groups themselves as layers:
local function CountLayers(layers)
local sum = 0
for _, layer in ipairs(layers) do
if layer.layers == nil then
sum = sum + 1
else
sum = sum + CountLayers(layer.layers)
end
end
return sum
end
-- The rest of the code...
newLayer.name = "Layer " .. CountLayers(sprite.layers)
So the result looks more like this:
2 Likes