[Maya-Python] Cycle Warning with ikHandle and getAttr(time=1)

206 views
Skip to first unread message

Marcus Ottosson

unread,
Aug 14, 2018, 9:29:16 AM8/14/18
to python_in...@googlegroups.com

Hi all,

TLDR; In Maya 2015-2018, getAttr returns stale value when used in conjunction with an ikHandle

I’ve ventured into the depths of querying attributes at an arbitrary time..

# Example
from maya import cmds
cmds.getAttr("myNode.translateX", time=5)

And it has worked well so far, except for when IK is involved.

from maya import cmds

# Build Scene
# o      o
#  \    /
#   \  /
#    o
#
cmds.file(new=True, force=True)
a = cmds.joint(name="Root")
cmds.move(0, 0, 20)
cmds.joint()
cmds.move(0, 0, 0)
b = cmds.joint()
cmds.move(0, 10, -20)
handle, eff = cmds.ikHandle(
    solver="ikRPsolver",
    startJoint=a,
    endEffector=b,
)

assert cmds.getAttr(a + ".rx") == 0.0
assert cmds.getAttr(a + ".rx", time=1) == 0.0  # Cycle warning!

Suddenly, Maya throws a cycle warning. Why is that?

Furthermore, the value returned isn’t right anymore.

# Move handle
cmds.move(0, 15, -10, "|ikHandle1")

assert round(cmds.getAttr(a + ".rx"), 0) == -14.0
assert cmds.getAttr(a + ".rx", time=1) == 0.0  # Bad value, no warning..

Where I should see -14.0 I get the previous value, 0.0. Why is that?

And so the story goes, until..

# Move root
cmds.move(0, 0, 21, a)

assert round(cmds.getAttr(a + ".rx"), 0) == -14.0
assert round(cmds.getAttr(a + ".rx", time=1), 0) == -14.0  # Correct

Viola, all values are correct. Why is that?? :O

This happens consistently across all versions of Maya, except 2015 doesn’t throw a Cycle Warning.

Anyone stumbled upon this before?

Joe Weidenbach

unread,
Aug 14, 2018, 4:13:20 PM8/14/18
to python_in...@googlegroups.com
I call it a bug, but it's been around a loooong time.

Maya updates the ikHandles in a separate pass (there's just one solver for multiple handles), and I don't believe that script mode forces an update on the solver, where moving the root updates the DAG directly.

It's one of the several places where the maya internals bypass the DAG so that they can work (IK handles don't put input connections on the joint rotations as that _would_ be a cycle, so the update needs to live in a callback somewhere, which isn't triggered by your code). Another place where things get weird is with editable animation curves (the ones where you've got an editable curve in the scene that updates the animation curves).

Long story short, querying an attribute at a given time only works for direct DAG evaluation.  Anything which operates outside of the DAG won't be updated when you call ```getAttr```.  For those cases, you actually have to set the time and refresh the evaluation prior to the ```getAttr``` call:

```
from maya import cmds

# Build Scene
# o      o
#  \    /
#   \  /
#    o
#
cmds.file(new=True, force=True)
a = cmds.joint(name="Root")
cmds.move(0, 0, 20)
cmds.joint()
cmds.move(0, 0, 0)
b = cmds.joint()
cmds.move(0, 10, -20)
handle, eff = cmds.ikHandle(
    solver="ikRPsolver",
    startJoint=a,
    endEffector=b,
    forceSolver=True,
)

assert cmds.getAttr(a + ".rx") == 0.0
cmds.refresh(force=True) # Side note, this means your scene actually has to play through
assert cmds.getAttr(a + ".rx") == 0.0  # No Cycle warning anymore!

# Move handle
cmds.move(0, 15, -10, "|ikHandle1")
cmds.refresh(force=True)

assert round(cmds.getAttr(a + ".rx"), 0) == -14.0
assert cmds.getAttr(a + ".rx") == 0.0  # Correct value now, assertion is thrown
```

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmODV_eKp2%3DJ3AdUufuDfBe2qojSWFjNpT9gJO1A6e9OLhg%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Joe Weidenbach

unread,
Aug 14, 2018, 4:14:56 PM8/14/18
to python_in...@googlegroups.com

Helps to format :)

I call it a bug, but it’s been around a loooong time.

Maya updates the ikHandles in a separate pass (there’s just one solver for multiple handles), and I don’t believe that script mode forces an update on the solver, where moving the root updates the DAG directly.

It’s one of the several places where the maya internals bypass the DAG so that they can work (IK handles don’t put input connections on the joint rotations as that would be a cycle, so the update needs to live in a callback somewhere, which isn’t triggered by your code). Another place where things get weird is with editable animation curves (the ones where you’ve got an editable curve in the scene that updates the animation curves).

Long story short, querying an attribute at a given time only works for direct DAG evaluation. Anything which operates outside of the DAG won’t be updated when you call getAttr. For those cases, you actually have to set the time and refresh the evaluation prior to the getAttr call:

Marcus Ottosson

unread,
Aug 15, 2018, 2:44:05 AM8/15/18
to python_in...@googlegroups.com
Thanks Joe, that confirms my own research into this. I'm seeing traces of this all the way back in Maya 6.0, and not just related to IK, but time-related queries in general.


Challenging, to say the least..

Marcus Ottosson

unread,
Aug 17, 2018, 4:18:16 AM8/17/18
to python_in...@googlegroups.com

Ok, I’ve got a workaround.

  • Whenever you query an attribute at a given time, query it at time - 1 first

Now the result is always correct, with or without IK, at the expense of one more query.

Example

One quick way of achieving the effect is wrapping any call to getAttr with this.

def getAttr(attr, time):
    cmds.getAttr(attr, time=time - 1)
    return cmds.getAttr(attr, time=time)

Full working example

from maya import cmds

# Build Scene
# o     o
#  \   /
#   \ /
#    o
#
cmds.file(new=True, force=True
)
a = cmds.joint(name="Root", position=(0, 0, 20))
b = cmds.joint(position=(0, 0, 0), relative=False)
c = cmds.joint(position=(0, 10, -20), relative=False)
handle, eff = cmds.ikHandle(
    solver="ikRPsolver",
    startJoint=a,
    endEffector=c,
)

def getAttr(attr, time):
    cmds.getAttr(attr, time=time - 1)
    return cmds.getAttr(attr, time=time)

assert cmds.getAttr(a + ".rx") == 0.0
assert cmds.getAttr(a + ".rx", time=1) == 0.0

# Move handle
cmds.move(0, 15, -10, "|ikHandle1")

assert round(cmds.getAttr(a + ".rx"), 0) == -14.0
assert round(getAttr(a + ".rx", time=1), 0) == -14.0

# The above call also enables this other node to evaluate properly
# Which means we only need to call the special function once
# per IK node.
assert round(cmds.getAttr(b + ".rx", time=1), 0) == 49.0

cmds.move(0, 15, -15, "|ikHandle1")

assert round(getAttr(a + ".rx", time=1), 0) == -4.0

This (luckily) works consistently across all current versions of Maya.

It doesn’t quite jive with my understanding of IK and what Joe mentioned, that it uses this internal callback. This workaround shouldn’t work, so I’m a little weary still. Finally, I found that the Cycle Warning only happens on Maya 2018, no other version; not even 2019. So I’m tempted to just ignore it.

Marcus Ottosson

unread,
Aug 17, 2018, 4:19:12 AM8/17/18
to python_in...@googlegroups.com
Oh, I should have added, this also applies to querying attributes via the API and the MDGContext. Phew!

Joe Weidenbach

unread,
Aug 17, 2018, 6:42:39 AM8/17/18
to python_in...@googlegroups.com
Interesting.  I should note, the callback is an assumption on my part.  There's no connection on the dependency graph, so there has to be an internal system evaluating the handle, else it wouldn't work.  Glad you found a workaround though, I'll have to have a play with that.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

Marcus Ottosson

unread,
Aug 17, 2018, 6:54:32 AM8/17/18
to python_in...@googlegroups.com

I would make that assumption too. There’s one more thing hinting towards it being outside of the overall DAG loop, which is that changes happening in response to IK don’t trigger callbacks, in particular the maya.api.MDagMessage.addWorldMatrixModifiedCallback.


Reply all
Reply to author
Forward
0 new messages