Technical question

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

Moderators: Víctor Paredes, Belgarath, slowtiger

User avatar
synthsin75
Posts: 10280
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Technical question

Post by synthsin75 »

How do you get a layer's, shape's, point's, internal ID? All I can find in the scripting reference is how you select by ID. So far, I've only been able to provide an ID by using the few things I can find that actually return one. (i.e. moho.layer:Parent will return the parent layer's ID)

I'm trying to move through the layers (vector) of a group using SetSelLayer(). Unless there is any other way to get info from a layer, for a tool, I need to provide this with the layer ID.
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

Below is a snippet of code from a tool script I created recently that goes through layers in a switch using the arrow keys. This should work with any group layer as well.

First it checks for the keydown event (up arrow, down arrow). Then it counts how many layers are in the parent group layer, determines which layer it is on by comparing the name of the selected layer to all the other layers. Remember the selected layer is still the "main layer":

Code: Select all

local layer = moho.layer
but I also can access all the other layers in the same group buy using the parent:

Code: Select all

local pLayer = layer:Parent()
That is the part you may want to look at. The other stuff is to keep from doing anything with the up/down arrow keys when reaching the last or first layers in the group.

Code: Select all

function HV_Layer_Selector:OnKeyDown(moho, keyEvent, view)
	local layer = moho.layer
	if (keyEvent.keyCode == LM.GUI.KEY_DOWN) then
            local pLayer = layer:Parent()
            for i= 0, pLayer:CountLayers()-1 do
                local sLayer = pLayer:Layer(i)
                local x = layer
                if(layer:Name() == sLayer:Name()) then
                    if(i > 0) then
                        x = pLayer:Layer(i-1)
                    end
                    moho:SetSelLayer(x)
                end
            end
	end
	if (keyEvent.keyCode == LM.GUI.KEY_UP) then
            local pLayer = layer:Parent()
            for i= 0, pLayer:CountLayers()-1 do
                local sLayer = pLayer:Layer(i)
                local x = layer
                if(layer:Name() == sLayer:Name()) then
                    if(i < pLayer:CountLayers()-1) then
                        x = pLayer:Layer(i+1)
                    end
                    moho:SetSelLayer(x)
                end
            end
	end
end
A layer ID is useless when using SetSelLayer(layer) function. It needs an ACTUAL layer object or it won't work. Also moho.layer.Parent() returns a GROUP LAYER object, not an ID. It is an actual layer reference.

A tip on reading the scripting reference:

Code: Select all

GroupLayer Parent()
"GroupLayer" indicates what is returned by the function. Sometimes you see the word "void" in front of the function. This means that function does not return a value. This is helpful to understand what each of those functions gives you. It is important because if you don't have the right type of object and use it somewhere else it won't work.

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

Post by heyvern »

Additional info:

If you have no group layers you get a layer using:

Code: Select all

for i=0, moho.document:CountLayers() - 1 do
        local tLayer = moho.document:Layer(i)
..... do stuff to this layer or put it in a table or variable etc....
.......
.....
end
In the code above the layers are accessed at the DOCUMENT level. The TOP level. That is how you would get layers not in a group.

------

A little more explanation for the code in the previous post:

Here I assign the variable "x" as a sub layer inside the parent layer. pLayer:Layer() always returns a layer object (of course "pLayer" is a variable I created from "moho.layer:Parent()"). You CAN use ID numbers to get to a layer but you MUST get a layer object using the format that will RETURN a layer object not just the ID.

Lots of times I will just store a number ID value in a table... but sometimes I store the actual layer itself. These types of values are called "userdata". If you tried to print to string the value of a layer object you would get a bunch of numbers and letters that are the internal reference to that object. You would not get the ID number.

Code: Select all

                    if(i > 0) then
                        x = pLayer:Layer(i-1)
                    end
                    moho:SetSelLayer(x)
                end
Have I totally confused you yet? ;)

-vern
User avatar
synthsin75
Posts: 10280
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Thank you, thank you, thank you! I'll have to read over that better tomorrow, but you already cleared a few things up. I had already done that 'print to string' on the layer (and group) objects (userdata).

So what I need is the object, and I think you've provided the code for doing just that. I had been thinking that the ID was the internal reference.

So the ID is just the layer order in the document, the layer object is the internal reference to the actual layer, and the layer name is what you see in the UI?


With this new knowledge, I'm pretty confident I'll have a new tool done ....oh....in a week or two (knowing my pace). :wink:
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

synthsin75 wrote: So the ID is just the layer order in the document and the layer object is the internal reference to the actual layer
Yes:

Code: Select all

moho.layer
... is a layer object. It is the layer selected. It is similar to how you access bones in the skeleton:

Code: Select all

local skel = moho:Skeleton()
So now you have variable "skel" which is a shortcut to the skeleton of the selected layer. You access bones inside the skeleton:

Code: Select all

for i = 0, skel:CountBones() - 1 do
         local bone = skel:Bone(i)
         do suff....
       .......
or this if you know the ID number of the bone:

Code: Select all

         local bone = skel:Bone(27)
So a "skeleton" for a bone layer is similar to the "document" for top level layers or a "group" containing layers. You first need to determine where you are looking for individual layers (in the document or a group) then you have to count them and pick one or more out based on some criteria.

One thing different though. Bones have a way to get the ID from the bone object directly:

Code: Select all

local boneID = skel:BoneID(bone)
I have a bone object but I want the ID. Easy with that one function. Layers don't have that function. It does not exist. A layer still has an internal ID but you can't access it directly as you can with bones in the skeleton. You have to use a "for loop" to find the ID of a layer first. You can only work with layer objects to modify them.

Here are two ways to get a layer "object":

Code: Select all

for i=0, moho.document:CountLayers() - 1 do
        local layer = moho.document:Layer(i)
        do suff....
       .......
or this, if you know exactly what layer you want to target (layer 6):

Code: Select all

local layer = moho.document:Layer(6)
In conclusion

So bones actually have more ways to access them than layers. They have the bone object and the bone ID and you can convert them back and forth. If you for instance have a bone ID you can create a variable that is a bone object. If you have a bone object and just need the ID you can find the ID.

Layers on the other hand can only be accessed by ID and you can't get the ID from the layer object. You either find it using a "for loop" and checking the order or name, or looking at the layer palette and counting the layers. You can't ask for the ID of a layer from a layer object. This is a bit annoying but not a deal breaker. It just requires a bit more coding but you end up with the same thing.
and the layer name is what you see in the UI
Yes. And you can get the name of a layer by assigning it to a variable (layerName in this example):

Code: Select all

local layerName = moho.layer:Name()
Hope this helps out.

-vern
User avatar
synthsin75
Posts: 10280
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Making progress...

Does anyone know if there is a moho scripting reference that is indexed by terms? It seems to take me forever to find some things as is. Just wondering.


I've almost got this working (at least this first bit), but I can't seem to figure out what's not quite right. :roll:
User avatar
synthsin75
Posts: 10280
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Woohoo!!!!! :shock: :D

I was just about to post a question, but while I was writing it, a solution occurred to me. And it works! I've been trying to be tight lipped about this until I could verify that it would work. And it does.

I'm not quite ready to post the script yet (as I have more I still want to do with it), but I can let the cat out of the bag. Sorry, I may be too excited about this. :oops:


What it does:

This script allows you to select layers by clicking on a visible shape in that layer. (I think Slowtiger had recently mentioned this functionality of other apps.) In other words, if you have a group (bone etc.) filled with vector layers and you have one of those vector layers selected (for the time being anyway), clicking with this tool will select any layer in the group that has a shape at that location.

Eventually this will become part of a universal shape/layer tool, but there's still a lot of work to get that far. As a standalone tool, I guess I could modify other scripts to call it with a modifier key, but I don't know if I'll bother doing that.

I still have to work out some implementation issues. Right now, if you click overlapping shapes, you don't really have a say of which one (shape & layer) it selects.

Since a 'for' loop only evaluates its expressions once, how would I go about having the loop be dependant on the current or last selection? In other words, looping through the layers, I need it to always start from the current layer down and then cycle back to catch the higher layers if no shape is found.
:wink:
User avatar
Víctor Paredes
Site Admin
Posts: 5818
Joined: Wed Jan 26, 2005 12:18 am
Location: Barcelona/Chile
Contact:

Post by Víctor Paredes »

synthsin75 wrote:This script allows you to select layers by clicking on a visible shape in that layer. (I think Slowtiger had recently mentioned this functionality of other apps.) In other words, if you have a group (bone etc.) filled with vector layers and you have one of those vector layers selected (for the time being anyway), clicking with this tool will select any layer in the group that has a shape at that location.

Eventually this will become part of a universal shape/layer tool, but there's still a lot of work to get that far. As a standalone tool, I guess I could modify other scripts to call it with a modifier key, but I don't know if I'll bother doing that.

I still have to work out some implementation issues. Right now, if you click overlapping shapes, you don't really have a say of which one (shape & layer) it selects.

Since a 'for' loop only evaluates its expressions once, how would I go about having the loop be dependant on the current or last selection? In other words, looping through the layers, I need it to always start from the current layer down and then cycle back to catch the higher layers if no shape is found.
:wink:


:shock: wow :D
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

FAN-FREAKING-TASTIC!!!!

You beat me to it!!! I was just thinking about playing around with a script like that!

When you are ready there is a really really really easy way to stick a reference to your new tool script inside any other tool script. Of course it means modifying the other tool but it's only like two or three lines and very simple. I use this all the time.

Basically you just add the the keydown reference to any tool that you want that functionality.

I have a tool (HV_Layer_Selector) that uses the arrow keys to move up and down the layers in a switch layer. I just added this code to the select point and translate points tool "OnKeyDown()" function:

Code: Select all

function FA_SelectPoints:OnKeyDown(moho,keyEvent)
            HV_Layer_Selector:OnKeyDown(moho, keyEvent, view)
.... other stuff....
The HV_Layer_Selector tool has a keydown function in it. I am calling the keydown function of that tool from another tool. The current tools in AS do this already. The different bone tools will call fucntions in the bone selection tool. Even if the tool doesn't already have a keydown function just stick one in there and add the tool you want to USE the keydown function from.

You could even create a utility script, put the keydown function in there and reference that instead:

Code: Select all

function FA_SelectPoints:OnKeyDown(moho,keyEvent)
-- **************************************************
-- OnKeyDown - New code for Bone Groups
-- **************************************************
	HV_SharedUtils:OnKeyDown(moho, keyEvent)
	moho:UpdateUI()
	keyEvent.view:DrawMe()
.... more stuff....
-vern
User avatar
synthsin75
Posts: 10280
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

You beat me to it!!! I was just thinking about playing around with a script like that!
:lol: Yeah, that's why I'm keeping it a bit under wraps until its ready. I'm afraid one of you scripting pros will run away with it, and spoil all my fun.

Yeah I've seen plenty of scripts that call others. I might do that once I get some more things worked out.

First, I still need to:

1) Get it to do nothing if no shape exists below mouse click.

2) Solve some slight issues with multiple shapes on a layer, including getting it to deselect the previous shape on the same layer.

Once these are done (and perhaps a few more), I plan on implementing a 'shape sort' functionality for layers. Does anyone know what the channel code for the 'layer sort channel' would be?

:wink:
User avatar
synthsin75
Posts: 10280
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

I have a tool (HV_Layer_Selector) that uses the arrow keys to move up and down the layers in a switch layer.
I thought it was funny you posted that as your examples to my question. On top of everything else, Vern is psychic :shock:....., or is it psychotic? :lol:
User avatar
synthsin75
Posts: 10280
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Okay, this is probably to the point of being useful, so I went ahead and added it to a modified select shape tool.

Using the syn_select_shape tool, you can hold CTRL+SHIFT and clicking on a visible shape (within the current layer's group), and that shape's layer will be selected.

Overlapping shape-layers are selectable from the top layer down, so if a lower shape is covered, you have to find an exposed edge to select its layer.

Note: If you CTRL+SHIFT select over no shape, it will default to the bottom layer. I haven't yet figured out how to make it stay where it is.


Let me know what you think, and if I should go ahead and post this for general use. :wink:

Download it here.
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

Oh wow! This is.... WICKED COOL BABY! I LOVE IT! Yeehaaaa!

I will be adding this to my FA vector tools. This is really great. Very very very useful. A great time saver!

p.s. I poked around in the script reference looking for a way to NOT select a layer if the "eyeball" is turned off... dagnabbit... no way to do it. I just can't find any reference for the layer being "on" or not. This could eliminate the problem with overlapping layers.

A really cool simple workaround is to just have a tiny filled box on each layer making sure they don't overlap so you can select them.

This is great. I've always had this idea in the back of my head as a great time saver. Thanks dude!

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

Post by heyvern »

Suggestions;

I haven't looked at the code yet so I don't know exactly how you are picking the layers. I am guessing you are using the SHAPE to determine which layer is selected.

What about using a point in the mesh of the layer instead of the shape? For instance the select points tool can select the nearest point to the mouse click. Not sure if this is actually "better". It may cause difficulties with points close to each other on several layers... just brainstorming.

One thing it would do is allow the switching of layers when fills are "turned off" in the display settings. The layer switching won't work if fills are not visible (or don't exist). Not a big deal really... even Adobe Illustrator has problems with this when in outline view. ;)

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

Post by heyvern »

I figured out how to stay on the current layer if no shape was picked:

First I created another variable called "prevLayer" in that spot where all the other variables are created. This var is the layer that is selected at that moment before actually clicking anywhere:

Code: Select all

  local prevLayer = moho.layer

Then I added a single "else" to the last bit that sets the selected layer to that layer if no shape was clicked:

Code: Select all

    if (shapefound == true) then
      break
    else
      moho:SetSelLayer(prevLayer)
    end
Bingo! Works like a charm!

-vern
Post Reply