We started by creating a function which would create an object on stage by recursion:
import maya.cmds as cmds
def fakeRecursion( iterations ):
for i in range(iterations): #range(0, iterations, 1)
#create the objects
cmds.circle( n="myCircle_%d" % i )
cmds.nurbsSquare( n="mySquare_%d" % i )
#rotate objs
cmds.rotate(i*10, i*10, i*10, "myCircle_%d"%i)
cmds.rotate(-i*10,- i*10,- i*10, "mySquare_%d"%i)
#move objs
cmds.move(0,0,i/10, "myCircle_%d"%i)
cmds.move(0,0,i/10, "mySquare_%d"%i)
#collect points for polyFacet
pos1 = cmds.pointPosition("topmySquare_%d.cv[0]" %i)
pos2 = cmds.pointPosition("myCircle_%d.cv[0]" % i)
pos3 = cmds.pointPosition("rightmySquare_%d.cv[0]" %i)
pos4 = cmds.pointPosition("myCircle_%d.cv[1]" % i)
pos5 = cmds.pointPosition("bottommySquare_%d.cv[0]" %i)
pos6 = cmds.pointPosition("myCircle_%d.cv[2]" % i)
pos7 = cmds.pointPosition("leftmySquare_%d.cv[0]" %i)
pos8 = cmds.pointPosition("myCircle_%d.cv[3]" % i)
cmds.polyCreateFacet(n="myFacet_%d" % i,
p=[pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8]
)
cmds.group("myCircle_%d" % i,
"mySquare_%d" % i,
"myFacet_%d" % i )
fakeRecursion(30)
As we saw, the code above works, but has a series of coding problem which makes it non-flexible and difficult to read and modify. The first modification we did was to convert the recursion, which in this case was made by using a for loop, to what we could call “real” recursion, when the function calls itself:
def realRecursion( iterations ):
i = iterations
#create the objects
cmds.circle( n="myCircle_%d" % i )
cmds.nurbsSquare( n="mySquare_%d" % i )
#rotate objs
cmds.rotate(i*10, i*10, i*10, "myCircle_%d"%i)
cmds.rotate(-i*10,- i*10,- i*10, "mySquare_%d"%i)
#move objs
cmds.move(0,0,i/10, "myCircle_%d"%i)
cmds.move(0,0,i/10, "mySquare_%d"%i)
#collect points for polyFacet
pos1 = cmds.pointPosition("topmySquare_%d.cv[0]" %i)
pos2 = cmds.pointPosition("myCircle_%d.cv[0]" % i)
pos3 = cmds.pointPosition("rightmySquare_%d.cv[0]" %i)
pos4 = cmds.pointPosition("myCircle_%d.cv[1]" % i)
pos5 = cmds.pointPosition("bottommySquare_%d.cv[0]" %i)
pos6 = cmds.pointPosition("myCircle_%d.cv[2]" % i)
pos7 = cmds.pointPosition("leftmySquare_%d.cv[0]" %i)
pos8 = cmds.pointPosition("myCircle_%d.cv[3]" % i)
cmds.polyCreateFacet(n="myFacet_%d" % i,
p=[pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8]
)
cmds.group("myCircle_%d" % i,
"mySquare_%d" % i,
"myFacet_%d" % i )
#recursion part
if iterations == 0:
return "Done."
else:
iterations -= 1
realRecursion(iterations)
realRecursion( 30 )
As we see it keeps producing the same result as the function before. Also, in terms of speed and memory, the difference is not noticeable. But as we saw, when you start to use complex and long functions, the use of “real” recursion is regarded as much more efficient.
The second step on the optimization of the function was to remove all the hard-coded bits. This allows for greater flexibility, as we don’t really need to care about the names of the created objects, simply by using variables to store them and to refer to them later:
def realRecursionOpt1( iterations ):
i = iterations
#create the objects
myCircle = cmds.circle( )
mySquare = cmds.nurbsSquare( )
#rotate objs
cmds.rotate(i*10, i*10, i*10, myCircle )
cmds.rotate(-i*10,- i*10,- i*10, mySquare)
#move objs
cmds.move(0,0,i/10, myCircle )
cmds.move(0,0,i/10, mySquare )
#collect points for polyFacet
#select mySquare
cmds.select( mySquare, r=1 )
sides = cmds.filterExpand( sm=9 )
pos1 = cmds.pointPosition( sides[0]+".cv[0]" )
pos2 = cmds.pointPosition( myCircle[0] + ".cv[0]" )
pos3 = cmds.pointPosition( sides[1]+".cv[0]" )
pos4 = cmds.pointPosition( myCircle[0] + ".cv[1]")
pos5 = cmds.pointPosition( sides[2]+".cv[0]" )
pos6 = cmds.pointPosition( myCircle[0] + ".cv[2]")
pos7 = cmds.pointPosition( sides[3]+".cv[0]" )
pos8 = cmds.pointPosition( myCircle[0] + ".cv[3]")
myFacet = cmds.polyCreateFacet(
p=[pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8]
)
cmds.group( myCircle, mySquare, myFacet )
#recursion part
if iterations == 0:
return "Done."
else:
iterations -= 1
realRecursionOpt1(iterations)
realRecursionOpt1( 30 )
As you can see, we do not have to know any names of objects if we store them in variables and use them to refer to them later.
As a last step on the optmization process, we saw how when you repeat a command too many times, it almost always means you can convert it into a loop. The same is valid for numbers or strings which are being repeated too many times: you should always recur to variables or arguments to define values which are used several times:
def realRecursionOpt2( iterations , amt=10 ):
i = iterations
#create the objects
myCircle = cmds.circle( )
mySquare = cmds.nurbsSquare( )
#rotate objs
cmds.rotate(i*amt, i*amt, i*amt, myCircle )
cmds.rotate(-i*amt,- i*amt,- i*amt, mySquare)
#move objs
cmds.move(0,0,i/amt, myCircle )
cmds.move(0,0,i/amt, mySquare )
#collect points for polyFacet
#select mySquare
cmds.select( mySquare, r=1 )
sides = cmds.filterExpand( sm=9 )
#create a list to store the positions
positions = []
for i in range( len(sides) ):
pos1 = cmds.pointPosition( sides[i] +".cv[0]" )
pos2 = cmds.pointPosition( myCircle[0] + ".cv[%d]" % i )
positions.append(pos1)
positions.append(pos2)
myFacet = cmds.polyCreateFacet(
p=positions
)
cmds.group( myCircle, mySquare, myFacet )
#recursion part
if iterations == 0:
return "Done."
else:
iterations -= 1
realRecursionOpt2(iterations, iterations )
realRecursionOpt2( 30 )
Another thing we saw on the example above is the creation and usage of optional function arguments, in this case, amt=10. In this case, as we already assign a value to amt in the function definition, it means that if you don’t pass this argument in the function call, Maya will assume its value as 10. In case you do supply an argument, Maya will use this value instead.