Moar Minq!
Posted on Sun 16 October 2016 in blog
After the recent updates to mGui, I’ve just pushed a few changes to minq. After a couple of months away I solved a couple of nagging issues with that had me stumped earlier.
Oh man, after a month of C++ it feels so nice to do somey Python, even if it’s just playing hooky…
Fabulous Fancy Filters
The big change is to the way you do attribute filters. In the first release, you had to choose between a nice clean way of writing an attribute filter and a limited set of features. The where()
operator on a query let you pass in a filter function , so you could — for example — find all the transforms above the zero line like this:
upper = Transforms().where(lambda p: cmds.getAttr(p + '.ty') > 0)
Under the hood that would look more or less like:
for each_node in cmds.ls(type='transform')
if cmds.getAttr(each_node + ".ty") > 50:
yield each_node
Which works fine. However for a big list it involves a potentially large number of independent getAttr
calls, which aren’t too fast. So we also added the option to format a special bulk query that would issue the same getAttr
on all of the items in your query at once, for a big speed boost. Those accelerated queries looked like this:
upper = Transforms().where(item.ty > 50)
This exploits an interesting behavior in Maya: if you select a whole bunch of items at once and do a getAttr
from the listener you’ll get a whole list of answers, not just one — and the cost of the query is correspondingly lower. Unfortunately, it doesn’t work for custom attributes: it only works for Maya’s built-in attributes.
So in the old version of minq you had to choose between a clean, fast bulk query or a wordy, slower but more flexible version using a lambda. To de-couple the style from the speed, I’ve added two new variants on the query syntax: native
will generate a fast bulk attribute query for built-in attributes, and custom
uses the same syntax but uses the slower-but-flexible approach instead. Thus
upper = Transforms().where(native.ty > 50) # a fast bulk query
customized = Shapes().where(custom.example == 1) # a slower general query
There’s also an option for tweaking the custom queries so they will or will not raise exceptions if the custom attribute isn’t found. The default behavior for missing attributes is simply not to pass the filter, but in some situations you may want to know for sure if your stream has items that are missing the attributes you expect.
Proud, Pleasing Positions
Another longstanding gripe that is partially cleaned up in this pass is bulk queries for object positions. It’s always been easy to get local positions with a simple query like
Selected().get(Transforms).get(AttribValues, "translate")
However there was no easy way to get world positions. This release adds the WorldPositions
and LocalPositions
classes which will return the positions:
camera_positions = Cameras().get(WorldPositions)
Under the hood these are extracted from the matrix
and worldMatrix
attributes so they work as speedy bulk queries. The main limitation is that the results will always come back in native Maya units (ie, in centimeters) so you may need to transform them if you’re working in another unit. Should you need to do that, here’s a handy hack using api.OpenMaya.MVector
:
in_meters = lambda p: MVector(p) / 100
Selected().get(Parents).get(WorldPositions).foreach(in_meters)
Along with LocalPositions
and WorldPositions
, access to the matrices also made it easy to add a LocalAxis
query. This will return the local X, Y or Z axis of a transform in either world or local space:
forwards = Meshes().get(Parents).get(LocalAxis, 'x')
By default the axis vectors will be returned in world space, but you can get local space instead:
lcl_forwards = Meshes().get(Parents).get(LocalAxis, 'x', local=True)
These vectors are extracted directly from the matrices, so you may need to renormalize them depending on what you want them for.
Dauntless Documentation
While we’re on the subject, I’ve cleaned up the minq wiki. After a long month of slogging through the Unreal Engine codebase in search of what-the-heck-is-going-on-here?, I’ve tried to atone for my past sins in terms of under-documenting. Please give me a shout out or update the wiki yourself if you see something wrong or unclear on the docs!