Rasheed? Anyone? Need a function to get all bone layers

Moho allows users to write new tools and plugins. Discuss scripting ideas and problems here.

Moderators: Víctor Paredes, Belgarath, slowtiger

Post Reply
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Rasheed? Anyone? Need a function to get all bone layers

Post by heyvern »

I understand the concept on an intellectual level but my thick crusty old brain can't get a handle on it.

I need a way to iterate the layer list and retrieve all the bone layers or (group layers I can work from that) no matter where they are or how deep inside other folders.

My problem is trying to create an iteration that can go into a group layer check those for groups and so on... until there are no more to check. I get closer and closer but always end up stopping short. I understand how it should work... I just get overwhelmed.

I know this involves some kind of "stateless iterator" or something... I just don't have the brains for it... my head... is... spinning... it hurts... it hurts.

No really... I got a bad headache the other night and had to go to bed.

-vern
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

Here is a simple solution:

Code: Select all

function LayerScript(moho)
    local tree = LayerNames(moho, moho.document, {}, 0, 0)
    for i in tree do
        print( tree[i].name .. ' (' .. tree[i].level .. ')' )
    end
    print('----------')
end

function LayerNames(moho, group, tree, level, count)
    for i = 0, group:CountLayers() - 1 do
        local layer = group:Layer(i)
        tree[count] = { name = layer:Name(), level = level }
        count = count + 1
        if layer:IsGroupType() then
            local group = moho:LayerAsGroup(layer)
            tree = LayerNames( moho, group, tree, level + 1, count)
        end
    end
    return tree
end
This script collects the layer names and their hierarchy level in the table tree. The table tree is an array counting from zero, to the number of layers, minus one. Each element of this array represents the layer properties. In effect, each array element is a table, containing the layer name and the hierarchy level in the layer tree.

You can just as easily collect all other features of a layer, such as layer type. Just put them in the table definition of tree[count], like I did with the layer name and hierarchy level. The i-th layer from the bottom of the Layer window can be accessed as follows:

Code: Select all

tree[i].name -- layer  name
tree[i].level -- hierarchy level of the layer in the layer tree
Here are an example with screenshots of the layer structure of a document and the Lua console output:

Image Image

If you want to know what the parent of a layer is, the code becomes a little more complicated:

Code: Select all

function LayerScript(moho)
    local tree = LayerNames(moho, moho.document, {}, 0, 0)
    print(table.getn(tree) + 1 .. ' layers :')
    for i = 0, table.getn(tree) do
        local parent = tree[i].parent
        if parent < 0 then
            parent = '(no parent)'
        end
        print( 'tree[' .. i .. '] = ' .. tree[i].name 
        .. ' (' .. tree[i].level .. ')' .. ' -> ' .. parent )
    end
    print('----------')
end

function LayerNames(moho, group, tree, level, count)
    local parent = count - 1
    for i = 0, group:CountLayers() - 1 do
        local layer = group:Layer(i)
        tree[count] = { 
            name = layer:Name(), 
            level = level, 
            parent = parent }
        count = count + 1
        if layer:IsGroupType() then
            local group = moho:LayerAsGroup(layer)
            tree = LayerNames( moho, group, tree, level + 1, count)
        end
    end
    return tree
end
If you don't understand the code, just tell me, and I try to do my best to explain.
Last edited by Rasheed on Sat Feb 10, 2007 12:04 am, edited 3 times in total.
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

I understand it perfectly.

That is my problem. I understand it if I see it and can step through in my head what is going on. Trying to come up with it from scratch is so much harder for me.

I suppose a novice car mechanic could fix something in a car but probably couldn't build a whole car from scratch. ;)

I can see from your code that I at least was on the right track... I still have a lot to learn about LUA tables and functions. I know enough to be dangerous (just look at the crash log on my Mac!).

Thank you thank you thank you!!!

-vern
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

Glad to be of service, Vern.

I added some code in my original message to give you the array index number of the parent layer.
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

Haha!

Now I know why I was having so much trouble!

Your script doesn't work if the order the layers were created is changed or shifted in the order of their levels.

For instance if the first bone layer created is placed inside another bone layer it isn't listed properly.

There seems to be a disconect somehow with the internal ordering of layers in AS and the iteration through those IDs.

If I create bone layer 2 and then create bone layer 3 and drag bone layer 2 inside bone layer 3

bone layer 2 never shows up in the iteration. It was created first and is now lower in the hierchy. It is skipped. Also group layers INSIDE bone layer 2 are also skipped... obviously since bone layer 2 is skipped.

This would explain some of my frustration and inconsistent results. Since my test file was a REAL WORLD file, one that I had been working on and moving things around, none of my iterations were working correctly.

YOUR CODE SHOULD WORK! But only if the layers are never moved around too much.

Is this a problem with AS layer ordering - moho:document:Layer(), or something to do with unordered tables in LUA?

Now that I know why it isn't working I can play around with it and maybe fix it.

-vern
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

Remember that the elements in Lua tables are not stored in a particular order.
heyvern wrote:If I create bone layer 2 and then create bone layer 3 and drag bone layer 2 inside bone layer 3

bone layer 2 never shows up in the iteration. It was created first and is now lower in the hierchy. It is skipped. Also group layers INSIDE bone layer 2 are also skipped... obviously since bone layer 2 is skipped.
Yes, I can see that. I should see why that is, and how to remove this bug.
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

I can't for the life of me see why this is happening. It seems strange indeed. The layers are suppose to be accessed by their order in the layer palette, according to the scripting reference.

moho.document:Layer(int)

That integer number is suppose to be the order in the layer palette starting with 0 ... or the order of the layers in a group starting with 0.

This should not be happening and I can't see how to get around it. The order of creation is somehow... changing how the layers are accessed in the Layer() table.

Weird.

I suppose the reference to the layers could be placed in a new table with a new index. I did this before with the layers. That is actually how I reference the layers in my script, by accessing a layer ID using a new unique index in a table so it matched the index of a text list.

-vern
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

I created tables:

toLayerList = {}
frmLayerList = {}

then put the layers in each list like this..
for i = 0, (moho.document:CountLayers()-1 do
toLayerList.name = moho.document:Layer(i):Name()
toLayerList.layerID =moho.document:Layer(i)
end


This seems to work since the index for the table was created using the for loop.

I can just adapt your script to this and see if it works better.

-vern
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

I removed the bug. The problem was that the count variable was not propagated, so instead of:

Code: Select all

tree = LayerNames( moho, group, tree, level + 1, count)
I had to use:

Code: Select all

tree, count = LayerNames( moho, group, tree, level + 1, count)
And of course both tree and count should be returned:

Code: Select all

return tree, count
That fixed the bug.

Here is the improved code:

Code: Select all

function LayerScript(moho)
    local tree, count = LayerNames(moho, moho.document, {}, 0, 0)
    print(count .. ' layers :')
    for i = 0, count - 1 do
        local parent = tree[i].parent
        if parent < 0 then
            parent = '(no parent)'
        end
        print( 'tree[' .. i .. '] = ' .. tree[i].name
        .. ' (' .. tree[i].level .. ')' .. ' -> ' .. parent )
    end
    print('----------')
end

function LayerNames(moho, group, tree, level, count)
    local parent = count - 1
    for i = 0, group:CountLayers() - 1 do
        local layer = group:Layer(i)
        tree[count] = {
            name = layer:Name(),
            level = level,
            parent = parent }
        count = count + 1
        if layer:IsGroupType() then
            local group = moho:LayerAsGroup(layer)
            tree, count = LayerNames( moho, group, tree, level + 1, count)
        end
    end
    return tree, count
end 
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

Shows you how much I know about this. ;)

Works like a charm!

Thanks!

-vern
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

YEEEHAAAA!

I just plugged in the new code to find all the bone layers into my copy/paste bones script... WOOOHOOO! It works!

Thanks Rasheed! Couldn't have done it without you!

I have a question though about where these functions go in a menu script, if you know that is.

...uh... where should these functions go in a menu script? At the top? The bottom? I put them at the top... only because they weren't working at the bottom... but then I was calling the starting function in the wrong spot.. and initializing the variables in the wrong spot... they work at the top now... should I move them down? It probably doesn't matter as long as they work.

Is there a proper "LUA way"? I think they should go at the bottom underneath all the actually functional stuff... When I say "top" I mean before the :Run and UI initialization code.

<sigh>

Forgive me. I'm a bit light headed. ;)

-vern
Post Reply