stupid question - Current layer ID? ID of moho.layer?

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

stupid question - Current layer ID? ID of moho.layer?

Post by heyvern »

For some reason I keep getting errors trying to do this:

Code: Select all

moho.document:Layer(moho.layer)
It tells me that moho.layer is not an integer. This is true and I think I understand it. How would I get an integer ID of the current layer?

I am trying to match up the ID with a text list and a table. I need to select the current layer from a text list in the UI. I can't use the text list label because there may be layers with the same name. I need to use an integer reference from a table to get the correct layer ID. The text list item is also based on the number of items in it so that won't match the layer table ID either.

So I store:

Code: Select all

...
moho.document:Layer(i)
...
.. in a for loop in a table for later access. This works fine except for finding the current layer in this table by ID.

Code: Select all

moho.layer
is not the same as

Code: Select all

moho.document:Layer([int])
If moho.layer is a MohoLayer object what is the ID?

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

Post by Rasheed »

Code: Select all

moho.document
is the document object

Code: Select all

moho.layer
is the object of the currently selected layer

Code: Select all

moho.document:Layer( i )
selects the i-th root level layer

Code: Select all

moho:document:CountLayers()
returns the number of root level layers

Code: Select all

for i = 0, moho.document:CountLayers() - 1 do
    local i = moho.document:Layer(i)
end
traverses the root layers

Code: Select all

IsGroupType()
is a member function that returns true if the layer object is a group type

Code: Select all

local group = moho:LayerAsGroup(layer)
local n = group:CountLayers()
turns the object layer into a group object (Group, Bone, Switch, or Particle layer), and stores the number of child layers of that group into n.

This means that with the Layer member function, you only get access to the layers in the level you have selected. At the root, there is moho.document. If there is a group type layer in the root, you need to create a group object to get to the next level of layers.
  • moho.document -> Layer(i) gives you the layers at the root, iterated over index value i
  • group layer -> Layer(i) gives you the layers inside that group
You seem to want to select each layer directly, but you cannot do this. You need to select a layer through its "group parent" object:

Code: Select all

local layer 
    = moho:LayerAsGroup(
        moho:LayerAsGroup(
            moho.document:Layer(i)
        ):Layer(j)
    ):Layer(k)
will give you the k-th layer object inside the j-th layer object inside the i-th root layer object.

However, if you want to compare moho.layer with some layer object, Lua throws an error, unless both objects are the same. The way around this is to use the pcall function of Lua, as is demonstrated in this layer script:

Code: Select all

function LayerScript(moho)
    local layer = moho.layer
    for i = 0, moho.document:CountLayers() - 1 do
        local layer = moho.document:Layer(i)
        --[[
        pcall(func, arg1, ...) calls the function 'func',
        with arguments 'arg1', etc. and return two values
        if no error occurred,
            the first value is true, 
            and the second the return value of the function
        if an error occurred,
            the first value is false,
            and the second value is the error message
        --]]    
        local ok, val = pcall(CmpLayers, layer, moho.layer)
        if ok and val then
            print("found " .. layer:Name())
        end
    end
end

function CmpLayers(layer1, layer2)
    if layer1 == layer2 then return true end
    return false
end
To traverse all layers in the current document, you need to start by traversing the root level (moho.document), and see if there are any group type layers with one or more child layers. If there are, you should traverse each group type layer, and see if there are any group type layers with one or more child layers. Etc. You can solve this, for instance, with a recursive algorithm and store the layers objects in a table, so you can access each layer object through a simple table without re-traversing the whole layer tree each time. I'm sure there are other methods than recursion. You could scan the current layer level, and push the current layer level to stack if you happen to find a group type layer with one or more child layers, create a new layer level, etc., until there are no more group type layers to scan, and then start popping the previous levels from stack, and continue where you left.

The trick is to device a data structure that captures the layer hierarchy structure, and is still easy to access. You could then add member functions to this object, e.g. to find the recipe how to traverse the layer hierarchy for the current layer (typically, the k-th layer of the j-th layer of the i-th root layer, or something similar).
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

Your code is going to help a lot. Thanks for that. I didn't take into account bone layers inside bones layers.

I just now found an easier way to compare the layers. Even when the layers were the same type I couldn't compare them to moho.layer so...

Code: Select all

print(tostring(moho.layer))
gives me this:

Code: Select all

0x11ba970
If I iterate through:

Code: Select all

moho.document:Layers(i)
print(tostring(i))
I get this result:

Code: Select all

0x11ba970
0x600f630
0x11a6750
0x116f590
0x6e50a20
If I do a DIRECT comparison to moho.layer I get an error "bad operand" since the tables are different types. But by comparing them as STRINGS it works. Each table is uniquely identified and I just compare them as strings to find the current selected layer in the whole list. It is always the same whether moho.layer or moho.document:Layer().

Is this bad? It seems to work and is very simple. Or am I missing something?

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

Post by Rasheed »

No, that could be a neat solution. Objects are stored in specific locations in computer memory (RAM), so you can use the object's memory location to uniquely identify them. Remember, however, that objects are allocated to memory dynamically, so you can only use this technique to check if two objects are the same at that moment in time. Storing memory values is not recommended, because they could change by the actions of your routines, or by an user action.

So I would only use it to confirm if the current layer (moho.layer) is the same object as the layer object returned by the Layer() member function, and not beyond that!
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

Yup I figured that was the case.

Since the copy/paste bone process would always take place during one "session" this won't be a problem. The table IDs during the process will remain the same.

What happens is this:

Launch the script from any layer. Script finds all the layers with bones (bone and switches) and puts each in two menus; Source and Destination.
(I now have your code to get all the bone layers. Will have to figure out how to display them to look like the layer hierarchy... maybe have to put spaces in front of child bone layer names.)

Choose a source layer for copying and a destination layer. Bones from the source layer menu are placed in a text list above it. Click on the bones in the first list to "copy" to the second list above the destination layer.

When the source layer is selected the script "selects" that layer to get the bone list for copying. When you are ready to paste the bones, the script switches to the destination layer.

If you are on a bone layer to start with the source layer is pre-selected in the menu and the bone lists are built. (you may have selected bones to copy already, those are loaded into the copy text list. saves time).

It's that bit that was proving funky. If you are ON a layer already (moho.layer) it still needs to be in both lists (source and destination can be the same bone layer). AND it has to be pre-selected from the menu list. So getting that current selected layer IDENTIFIED within the menu list was proving tricky.

It was easy to get the menu list item to match up with a layer ID using a table with a list of layers that aren't selected.

moho.layer wasn't playing well with this idea. The string comparison trick is quite handy. ;)

The whole copy stuff uses moho.Skeleton() so no problems there. As long as I switch layers at the right time.

I pretty much have to rewrite Tha Narie's original code. So much was geared towards two separate scripts running independently. The whole timing was getting a bit funky. My original "Frankenstein's monster" was trying to paste bones before they had been copied.

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

Post by heyvern »

Fascinating.

I will have to be careful with that table to string trick.

In your layerscript function code above, the moho.layer gets a completely different table identity than the same layer compared through the layer iteration.

When I do the same thing WITHIN THE SAME FUNCTION the identities remain the same.

So timing is very critical. Those table "IDs" change constantly. It will only work within the same function. Something a bit safer than "table to strings" might be better. Or create a variable right off the bat as the "currentLayer".

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

Post by Rasheed »

When you write "tables", you actually mean "userdata", because all Anime Studio objects are of type userdata. From your description I can deduce that there is a translation step between the actual objects in the Anime Studio application and the representation of those objects in the Lua interface. This means you refer to a reference to an object, instead to the object directly.
Post Reply