Home Website Youtube GitHub

Globally assign custom control types

Hi,

I’m new to mGear but have some experience in Python and Pymel. I have been experimenting a bit with mGear and it looks great!

I am wondering if there will be the possibility of globally setting the defaults shapes for controls based on their type. Taking arm_2jnt_01 as an example, I noticed the following pattern:

    self.fk1_ctl = self.addCtl(self.fk1_npo,
                               "fk1_ctl",
                               t,
                               self.color_fk,
                               **"cube",**
                               w=self.length1,
                               h=self.size * .1,
                               d=self.size * .1,
                               po=vec_po,
                               tp=self.fk0_ctl)

It looks like addCtl() method from shifter.component.Main ultimately calls a method in mgear.core.icon that creates a curve with parameters based on a string argument (in this case “cube”). Since “cube” is hard-coded into the arm_2jnt_01 code, the default shape for this type of control likely can’t be changed without doing some weird stuff like redefining the “cube” function to actually return a circle, or something similarly weird.

If such functionality were to be implemented, I believe a first step would be to define a global enum or dictionary mapping control “types” to shape arguments, like

CTL_SHAPES = {
    "FK": "cube",
    "IK_Eff": "circle",
    "IK_Pole": "flower",
    ...etc.
}

Or instead of a global, such a mapping could be assigned on a per-object basis, similar to how control colors are currently handled.

The addCtl() calls could then be modified to pass a dict value (e.g., CTL_SHAPES[“FK”]) instead of a literal string, while still preserving the ability to hardcode shapes for backwards-compatibility, if desired.

I guess this would require some kind of consensus on what the set of control types should be, though. Does this seem like something folks would be willing to implement? I’d be happy to help, with whatever time I can find.

2 Likes

Hi @Rynas Thanks for the feedback and idea. I think is a good one.
Let me some time to think about this and talk with other developers.

Cheers,
Miquel

Cool, thanks.

On a related note, I poked around in icon.py a little bit and noticed some functions have pymel Matrix() objects set as defaults. Example:

def cube(parent=None,
         name="cube",
         width=1,
         height=1,
         depth=1,
         color=[0, 0, 0],
         m=datatypes.Matrix(),  # <-- Object
         pos_offset=None,
         rot_offset=None):

Usually this is kind of dangerous because what it means is that an instance is constructed once when the function is defined. Here is a simple example of a function that just adds 1 to the first matrix element every time it’s called:

import pymel.core as pm

def myMatFunc(mat=pm.dt.Matrix()):
    mat[0] += pm.dt.Array([1, 0, 0, 0])
    return mat

Executing this repeatedly without overriding the default means that it continues to refer to (and modify) the same Matrix:

myMatFunc()[0]
# Result: Array([2.0, 0.0, 0.0, 0.0]) #
myMatFunc()[0]
# Result: Array([3.0, 0.0, 0.0, 0.0]) #
myMatFunc()[0]
# Result: Array([4.0, 0.0, 0.0, 0.0]) #

…which seems like probably not what’s desired? Or maybe I’m missing something about what’s intended for these functions?

[One could fix this by either not allowing a default, or by setting the default to None in the signature and assigning a new default object in the function body, like this:

def myMatFunc(mat=None):
    mat = mat or pm.dt.Matrix()
    mat[0] += pm.dt.Array([1, 0, 0, 0])
    return mat

…the ‘or’ works in this case because even a pm.dt.Matrix() with all zeroes evaluates to True.]

2 Likes