Maya transform node not appearing in list - python

The Maya python code below gives a nurbs boolean surface by first taking the difference of two nurbs spheres, nurbsSphere1 and nurbsSphere2, to give the nurbs surface nurbsBooleanSurface1. It then takes the difference of this surface and a third sphere, nurbsSphere3. The result, as seen in the outliner, is the three nurbs spheres plus a surfaceVarGroup, nurbsBooleanSurface1, which 'parents' three transform nodes nurbsBooleanSurface1_1, nurbsBooleanSurface1_2 and nurbsBooleanSurface1_3.
import maya.cmds as cmds
cmds.sphere(nsp=10, r=50)
cmds.sphere(nsp=4, r=5)
cmds.setAttr("nurbsSphere2.translateX",-12.583733)
cmds.setAttr("nurbsSphere2.translateY",-2.2691557)
cmds.setAttr("nurbsSphere2.translateZ",48.33736)
cmds.nurbsBoolean("nurbsSphere1", "nurbsSphere2", nsf=1, op=1)
cmds.sphere(nsp=4, r=5)
cmds.setAttr("nurbsSphere3.translateX",-6.7379503)
cmds.setAttr("nurbsSphere3.translateY",3.6949043)
cmds.setAttr("nurbsSphere3.translateZ",49.40595)
cmds.nurbsBoolean("nurbsBooleanSurface1", "nurbsSphere3", nsf=1, op=1)
print(cmds.ls("nurbsBooleanSurface1_*", type="transform"))
Strangley (to me), the list command, cmds.ls("nurbsBooleanSurface1_*", type="transform") only yields [u'nurbsBooleanSurface1_1', u'nurbsBooleanSurface1_2']; nurbsBooleanSurface1_3 is missing.
But when, after having executed the above code, the print command
print(cmds.ls("nurbsBooleanSurface1_*", type="transform"))
is re-executed, the result is [u'nurbsBooleanSurface1_1', u'nurbsBooleanSurface1_2', u'nurbsBooleanSurface1_3'].
I've tried delaying the execution of the final print command using time.sleep(n) to no avail. I've played with the idea that the missing node might have spun off into another namespace and then re-appeared at the completion of the execution block (desperate, I know!). I've experimented with renaming the spheres and surfaces, using functions and threads (the latter only superficially). The cause of the unlisted nurbsBooleanSurface1_3 on the first execution of
print(cmds.ls("nurbsBooleanSurface1_*", type="transform"))
remains a mystery. Any help would be much appreciated.

A dirty way (but only way I could find) is to call cmds.refresh() during the script.
I have rewritten your script here. Notice that I store each sphere in a variable, this is good practice to make sure it'll work, even if an existing object is already called nurbsSphere3 for example.
import maya.cmds as cmds
sphere1 = cmds.sphere(nsp=10, r=50)
sphere2 = cmds.sphere(nsp=4, r=5)
cmds.setAttr(sphere2[0] + ".translateX",-12.583733)
cmds.setAttr(sphere2[0] + ".translateY",-2.2691557)
cmds.setAttr(sphere2[0] + ".translateZ",48.33736)
nurbsBool1 = cmds.nurbsBoolean("nurbsSphere1", "nurbsSphere2", nsf=1, op=1)
sphere3 = cmds.sphere(nsp=4, r=5)
cmds.setAttr(sphere3[0] + ".translateX",-6.7379503)
cmds.setAttr(sphere3[0] + ".translateY",3.6949043)
cmds.setAttr(sphere3[0] + ".translateZ",49.40595)
nurbsBool2 = cmds.nurbsBoolean(nurbsBool1[0], sphere3[0], nsf=1, op=1)
cmds.refresh(currentView=True) # Force evaluation, of current view only
print(cmds.listRelatives(nurbsBool2[0], children=True, type="transform"))
When you create an object using cmds.sphere() it returns a list of the object name and more. To access this, you can use
mySphere = cmds.sphere()
print(mySphere)
# Result: [u'nurbsSphere1', u'makeNurbSphere1']
print(mySphere[0]) # the first element in the list is the object name
# Result: nurbsSphere1
The same is true for the boolean operation. Look in the documentation for the command under Return value http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/index.html

Related

Top 5 moves using chess.engine.SimpleEngine

I'm having trouble with the engine encapsulation of python-chess, I would like to use the Stockfish function top = stockfish.get_top_moves(5) but it seems like there is no way to do it using chess.engine.simpleEngine, do you have any advices?
I already tried getting all the result and then keeping just the top 5 move of the last evaluation using this piece of code:
self.engine.analysis(self.board, chess.engine.Limit(depth=18), multipv=5, root_moves = move_actions)
but it's tricky since the function is asyncronous and I'm integrating with other function that are not changeable I cannot make it asynchronous.
I'm going crazy trying to make it work, thanks to everybody.
The async object returned by that function is empty at first; you can wait until it is done by calling its .wait() method. Instead, if you don't want the async parts of engine.analysis, you can call engine.analyse which blocks until done and returns the result more directly. Both functions work to get the top 5 moves as you requested. Here is an example script:
import chess
import chess.engine
stockfish = chess.engine.SimpleEngine.popen_uci("<file path to engine>")
# Using engine.analysis
analysis_result = stockfish.analysis(chess.Board(), limit=chess.engine.Limit(depth=18), multipv=5)
analysis_result.wait() # This is the missing step
analysed_variations = analysis_result.multipv
# Or instead using engine.analyse
analysed_variations = stockfish.analyse(chess.Board(), limit=chess.engine.Limit(depth=18), multipv=5)
# Either way you now have a dict of results, with moves under "pv"
top_five_moves = [variation["pv"][0] for variation in analysed_variations]

Using python to create a bindSkin in Maya

I am trying to create a script that would help me automate the creation of a spine rig, but I am running into a problem. I am following the tutorial provided here and I am working on the step where you skin the curve to the IK joints.
However, when I try to use mc.bindSkin(), I keep getting an error:
Error: RuntimeError: file[directory]/maya/2016.5/scripts\createRigSpine.py line 200: Maya command error)
It's too late right now to for me to do much experimenting, but I was hoping someone could help me, or tell me if I'm using the wrong commands.
mc.select(crvSpine, jntIkMidSpine, jntIkChest)
mc.bindSkin(crvSpine, jntIkMidSpine, jntIkChest, tsb=True)
(have also tried mc.bindSkin() and mc.bindSkin(tsb=True))
Ideally, I want the settings to be:
Bind To: Selected Joints
Bind Method: Closest Distance
Skinning Method: Classic Linear
Normalize Weights: Interactive
Edit: I wanted to use skinCluster, not bindSkin.
you should use the skinCluster command to bind your curve to the joints - and you can actually do it without selecting anything!
Try this:
import maya.cmds as mc
influences = [jntIkMidSpine, jntIkChest]
scls = mc.skinCluster(influences, crvSpine, name='spine_skinCluster', toSelectedBones=True, bindMethod=0, skinMethod=0, normalizeWeights=1)[0]
# alternatively, if you don't want such a long line of code:
#
influences = [jntIkMidSpine, jntIkChest]
kwargs = {
'name': 'spine_skinCluster', # or whatever you want to call it...
'toSelectedBones': True,
'bindMethod': 0,
'skinMethod': 0,
'normalizeWeights': 1
}
scls = mc.skinCluster(influences, crvSpine, **kwargs)[0]
# OR just use the short names for the kwargs...
#
influences = [jntIkMidSpine, jntIkChest]
scls = mc.skinCluster(influences, crvSpine, n='spine_skinCluster', tsb=True, bm=0, sm=0, nw=1)[0]
If you wanted to, you could also explicitly set the weights you want for each cv of the curve. You could use the skinPercent command, or even just use setAttr for the various weight attrs in the skinCluster (that's a little more difficult, but not much)
cmds.bindSkin() command made for binding bones to geometry. It's not suitable for binding to IK's only. So you need to assign what joint you need to bind to.
For example:
import maya.cmds as mc
mc.select('ikHandle1','nurbsCircle1','joint5')
mc.bindSkin('ikHandle1','nurbsCircle1','joint5')
# the order of selection is vital
For constraining selected objects use the commands like this:
mc.pointConstraint('ikHandle1','nurbsCircle1', weight=5.0)
To find out what constraints are available to you, use Rigging module – Constrain menu – Parent, Point, Orient, Scale, Aim, Pole Vector.
I was using the wrong command. mc.skinCluster is what I wanted to use, not mc.bindSkin.

more efficient Python scripting in Blender3D

I am basically building a 3D scatter plot using primitive UV spheres and am running into memory issues when attempting to create more than a couple hundred points at one time. I am limited on my laptop with a 2.1Ghz processor but wanted to know if there is a better way to write this:
import bpy
import random
while count < 5:
bpy.ops.mesh.primitive_uv_sphere_add(size=.3,\
location=(random.randint(-9,9), random.randint(-9,9),\
random.randint(-9,9)), rotation=(0,0,0))
count += 1
I realize that with such a simple script any performance increase is likely negligible but wanted to give it a shot anyway.
Some possible suggestions
I would pre-calculate the x,y,z values, store them in a mathutil vector and add it to a dict to be iterated over.
Duplication should provide a smaller memory footprint than
instantiating new objects. bpy.ops.object.duplicate_move(OBJECT_OT_duplicate=(linked:false, TRANSFORM_OT_translate=(transform)
Edit:
Doing further research it appears each time a bpy.ops.* is called the redraw function . One user documentented exponential increase in time taken to genenerate UV sphere.
CoDEmanX provided the following code snippet to another user.
import bpy
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.mesh.primitive_uv_sphere_add()
sphere = bpy.context.object
for i in range(-1000, 1000, 2):
ob = sphere.copy()
ob.location.y = i
#ob.data = sphere.data.copy() # uncomment this, if you want full copies and no linked duplicates
bpy.context.scene.objects.link(ob)
bpy.context.scene.update()
Then it is just a case of adapting the code to set the object locations
obj.location = location_dict[i]

PyMel Rotate Raises Error

I want to randomly rotate a list of objects on a given axis with a random amount retrieved from a specified range.
This is what I came up with:
import pymel.core as pm
import random as rndm
def rndmRotateX(targets, axisType, range=[0,180]):
for obj in targets:
rValue=rndm.randint(range[0],range[1])
xDeg='%sDeg' % (rValue)
#if axisType=='world':
# pm.rotate(rValue,0,0, obj, ws=1)
#if axisType=='object':
# pm.rotate(rValue,0,0, obj, os=1)
pm.rotate(xDeg,0,0,r=True)
targetList= pm.ls(sl=1)
randRange=[0,75]
rotAxis='world'
rndmRotateX(targetList,rotAxis,randRange)
Im using pm.rotate() because it allows me to specify whether I want the rotations done in world or obj space (unlike setAttr, as far as I can tell).
The problem is, it raises this error when I try to run this:
# Error: MayaNodeError: file C:\Program Files\Autodesk\Maya2012\Python\lib\site-packages\pymel\internal\pmcmds.py line 140: #
It must be something with they way I enter the arguments for pm.rotate() (Im assuming this due to the line error PyMel spits out, which has to do with its arguments conversion function), but I cant figure out for the life of me wth I did wrong. :/
I think the problem is in this line
pm.rotate(rValue,0,0, obj, os=1)
obj should be the first argument, so it should be
pm.rotate(obj, (rValue,0,0), os=1)
but to make it even prettier you could use
obj.setRotation((rValue,0,0), os=1)
And also. Use pm.selected() instead of pm.ls(sl=1). It looks better
Another way to go about doing this..
from pymel.core import *
import random as rand
def rotateObjectsRandomly(axis, rotateRange):
rotateValue = rand.random() * rotateRange
for obj in objects:
PyNode(str(selected()) + ".r" + axis).set(rotateValue)
objectRotation = [[obj, obj.r.get()] for obj in selected()]
print "\nObjects have been rotated in the {0} axis {1} degrees.\n".format(axis, rotateValue)
return objectRotation
rotateObjectsRandomly("z", 360)
Since rand.random() returns a random value between 0 - 1, I just multiplied that by the rotateRange specified by the user..or in my preference I would just do away with that all together and just multiply it by 360...
You also don't need all the feedback I just think it looks nice when ran..
Objects have been rotated in the z axis 154.145898182 degrees.
# Result: [[nt.Transform(u'myCube'), dt.Vector([42.6541437517, 0.0, 154.145898182])]] #
Just as a straight debug of what you've got...
Issue 01: it's case sensitive
pm.rotate("20deg",0,0) will work fine, but pm.rotate("20Deg",0,0) will fail and throw a MayaNodeError because it thinks that you're looking for a node called '20Deg'. Basically, you want to build your string as per: xDeg='%sdeg' % (rValue)
Issue 02: you're relying on pm.rotate()'s implicit "will apply to selected objects" behaviour
You won't see this til you apply the above fix, but if you have two selected objects, and ran the (patched) rndmRotateX function on them, you'd get both objects rotating by the exact same amount, because pm.rotate() is operating on the selection (both objects) rather than a per-object rotation.
If you want a quick fix, you need to insert a pm.select(obj) before the rotate. And you possibly want to save the selection list and restore it...however IMHO, it's a Really Bad Idea to rely on selections like this, and so I'd push you towards Kim's answer.

Parallel mapping functions in IPython w/ multiple parameters

I'm trying to use IPython's parallel environment and so far, it's looking great but I'm running into a problem. Lets say that I have a function, defined in a library
def func(a,b):
...
that I use when I want to evaluate on one value of a and a bunch of values of b.
[func(myA, b) for b in myLongList]
Obviously, the real function is more complicated but the essence of the matter is that it takes multiple parameters and I'd like to map over only one of them. The problem is that map, #dview.parallel, etc. map over all the arguments.
So lets say I want to get the answer to func(myA, myLongList). The obvious way to do this is to curry, either w/ functools.partial or just as
dview.map_sync(lambda b: func(myA, b), myLongList)
However, this does not work correctly on remote machines. The reason is that when the lambda expression is pickled, the value of myA is not included and instead, the value of myA from the local scope on the remote machine is used. When closures get pickled, the variables they close over don't.
Two ways I can think of doing this that will actually work are to manually construct lists for every argument and have map work over all of the arguments,
dview.map_sync(func, [myA]*len(myLongList), myLongList)
or to horrifically use the data as default arguments to a function, forcing it to get pickled:
# Can't use a lambda here b/c lambdas don't use default arguments :(
def parallelFunc(b, myA = myA):
return func(myA, b)
dview.map_sync(parallelFunc, myLongList)
Really, this all seems horribly contorted when the real function takes a lot of parameters and is more complicated. Is there some idiomatic way of doing this? Something like
#parallel(mapOver='b')
def bigLongFn(a, b):
...
but as far as I know, nothing like the 'mapOver' thing exists. I probably have an idea of how to implement it ... this just feels like a very basic operation that there should exist support for so I want to check if I'm missing something.
I can improve a bit on batu's answer (which I think is a good one, but doesn't perhaps document in as much detail WHY you use those options). The ipython documentation is also currently woefully inadequate on this point. So your function is of the form:
def myfxn(a,b,c,d):
....
return z
and stored in a file called mylib. Lets say b,c, and d are the same during your run, so you write a lambda function to reduce it to a 1-parameter function.
import mylib
mylamfxn=lambda a:mylib.myfxn(a,b,c,d)
and you want to run:
z=dview.map_sync(mylamfxn, iterable_of_a)
In a dream world, everything would magically work like that. However, first you'd get an error of "mylib not found," because the ipcluster processes haven't loaded mylib. Make sure the ipcluster processes have "mylib" in their python path and are in the correct working directory for myfxn, if necessary. Then you need to add to your python code:
dview.execute('import mylib')
which runs the import mylib command on each process. If you try again, you'll get an error along the lines of "global variable b not defined" because while the variables are in your python session, they aren't in the ipcluster processes. However, python provides a method of copying a group of variables to the subprocesses. Continuing the example above:
mydict=dict(b=b, c=c, d=d)
dview.push(mydict)
Now all of the subprocesses have access to b,c,and d. Then you can just run:
z=dview.map_sync(mylamfxn, iterable_of_a)
and it should now work as advertised. Anyway, I'm new to parallel computing with python, and found this thread useful, so I thought I'd try to help explain a few of the points that confused me a bit....
The final code would be:
import mylib
#set up parallel processes, start ipcluster from command line prior!
from IPython.parallel import Client
rc=Client()
dview=rc[:]
#...do stuff to get iterable_of_a and b,c,d....
mylamfxn=lambda a:mylib.myfxn(a,b,c,d)
dview.execute('import mylib')
mydict=dict(b=b, c=c, d=d)
dview.push(mydict)
z=dview.map_sync(mylamfxn, iterable_of_a)
This is probably the quickest and easiest way to make pretty much any embarrassingly parallel code run parallel in python....
UPDATE You can also use dview to push all the data without loops and then use an lview (i.e. lview=rc.load_balanced_view(); lview.map(...) to do the actual calculation in load balanced fashion.
This is my first message to StackOverflow so please be gentle ;) I was trying to do the same thing, and came up with the following. I am pretty sure this is not the most efficient way, but seems to work somewhat. One caveat for now is that for some reason I only see two engines working at 100%, the others are sitting almost idle...
In order to call a multiple arg function in map I first wrote this routine in my personal parallel.py module:
def map(r,func, args=None, modules=None):
"""
Before you run parallel.map, start your cluster (e.g. ipcluster start -n 4)
map(r,func, args=None, modules=None):
args=dict(arg0=arg0,...)
modules='numpy, scipy'
examples:
func= lambda x: numpy.random.rand()**2.
z=parallel.map(r_[0:1000], func, modules='numpy, numpy.random')
plot(z)
A=ones((1000,1000));
l=range(0,1000)
func=lambda x : A[x,l]**2.
z=parallel.map(r_[0:1000], func, dict(A=A, l=l))
z=array(z)
"""
from IPython.parallel import Client
mec = Client()
mec.clear()
lview=mec.load_balanced_view()
for k in mec.ids:
mec[k].activate()
if args is not None:
mec[k].push(args)
if modules is not None:
mec[k].execute('import '+modules)
z=lview.map(func, r)
out=z.get()
return out
As you can see the function takes an args parameter which is a dict of parameters in the head nodes workspace. These parameters are then pushed to the engines. At that point they become local objects and can be used in the function directly. For example in the last example given above in comments, the A matrix is sliced using the l engine-local variable.
I must say that even though the above function works, I am not 100% happy with it at the moment. If I can come up with something better, I will post it here.
UPDATE:2013/04/11
I made minor changes to the code:
- The activate statement was missing brackets. Causing it not to run.
- Moved mec.clear() to the top of the function, as opposed to the end.
I also noticed that it works best if I run it within ipython. For example, I may get errors if I run a script using the above function as "python ./myparallelrun.py" but not if I run it within ipython using "%run ./myparallelrun.py". Not sure why...
An elegant way to do this is with partial functions.
If you know that you want the first argument of foo to be myArg, you can create a new function bar by
from functools import partial
bar = partial(foo, myARg)
bar(otherArg) will then return foo(myArg,otherArg)
let's build on that:
dview.map_sync(func, [myA]*len(myLongList), myLongList)
maybe the following would work:
from itertools import izip_longest
dview.map_sync(func, izip_longest(myLongList, [], fillvalue=myA))
example:
>>> # notice that a is a tuple
... concat = lambda a: '%s %s' % a
>>> mylonglist = range(10)
>>> from itertools import izip_longest
>>> map(concat, izip_longest(mylonglist, [], fillvalue='mississippi'))
['0 mississippi', '1 mississippi', '2 mississippi', '3 mississippi',
'4 mississippi', '5 mississippi', '6 mississippi', '7 mississippi',
'8 mississippi', '9 mississippi']
I am posting Alex S. comment as answer. This probably is right approach for this problem:
Just do partial application with a lambda. I know it looks weird, but using my_f = lambda a,my=other,arguments=go,right=here : f(a,my,arguments,right) is the simplest way to go about it without falling into pickling and pushing problems.

Categories