Search code examples
pythondecimalscalemayaarbitrary-precision

Maya Python... workaround for arbitrary precision when coding with maya.cmds


Maya commands library seems to dislike working with the built in decimal module in python. I am trying to scale an object by an arbitrarily large float for precision. As far as understanding that you can't scale by a decimal 'object' is clear.

ive been trouble shooting the math and it works fine when i type in the long float. as soon as its a decimal object maya wont have it.

so maybe there is a way to trick maya.cmds module that ive input the long float into the scale function. Is there

The following is a script for scaling an object based on the distance between 2 vertices on that object.

Here is the Code:

##  ((Ax - Bx)**2 + (Ay - By)**2 + (Az - Bz)**2)**0.5

import maya.cmds as mc
import decimal as dec
dec.getcontext().prec = 100 # arbitrary precision for floats

selPoints = mc.ls(sl = True)
objName = selPoints[0].split('.')[0]
print objName
vtxCoordList = mc.xform(selPoints, q = True, translation = True, ws = True)
Ax, Ay, Az = vtxCoordList[:-3]
Bx, By, Bz = vtxCoordList[3:]
d = dec.Decimal(((Ax - Bx)**2 + (Ay - By)**2 + (Az - Bz)**2)**0.5) #direct distance between both points

def getDist(*args):
    selPoints = mc.ls(sl = True)


    vtxCoordList = mc.xform(selPoints, q = True, translation = True, ws = True)
    Ax, Ay, Az = vtxCoordList[:-3]
    Bx, By, Bz = vtxCoordList[3:]
    d = dec.Decimal(((Ax - Bx)**2 + (Ay - By)**2 + (Az - Bz)**2)**0.5) #direct distance between both points
    dx = ((Ax - Bx)**2)**0.5
    print "Distance on X axis is: {0}".format(dx) #distance on X axis
    dy = ((Ay - By)**2)**0.5
    print "Distance on Y axis is: {0}".format(dy) #distance on Y axis
    dz = ((Az - Bz)**2)**0.5
    print "Distance on Z axis is: {0}".format(dz) #distance on Z axis
    mc.textFieldButtonGrp("currentLength", edit = True, tx = "{0}".format(d))    

## scale object to match measurement
def scaleDist():
    userInput = mc.textFieldButtonGrp("desiredLength",q = True, tx = True)
    scaleFactor = dec.Decimal(userInput)/dec.Decimal(d)

    mc.scale(dec.Decimal(scaleFactor), dec.Decimal(scaleFactor), dec.Decimal(scaleFactor), objName)

## window
mc.window("Distance Tool", t = "Distance Tool")
mc.rowColumnLayout(numberOfColumns = 2, columnAttach = (1, 'left', 0), columnWidth = [(1,100), (2,300)])
mc.text(l = "Current length")
currentLength = mc.textFieldButtonGrp("currentLength", ed = False, tx = "{0}".format(d), buttonLabel = "Refresh", bc = getDist)

mc.text(l = "Desired length")
desiredLength = mc.textFieldButtonGrp("desiredLength", buttonLabel = "Scale", bc = scaleDist)
mc.showWindow()

NOTE: select 2 vertices before running the script.

Cheers


Solution

  • Maya's native precision is usually based on 32-bit floats (for most linear distances and general purpose math) and 64-bit doubles (used mainly for angular values). Python floats are basically doubles, so they should be 'lossless' for all practical purpose although I've never tried to check that the python and C++ versions are not off by a bit due to implementation details.

    You can convert these to floats for use with maya.cmds with the float() builtin:

    dec = Decimal(1.0000000001)
    flt = float(dec)
    cmds.xform('pCube1', t = (flt, flt, flt))
    

    The maya python api is built around default python floats, so any number you pass in to maya has to be converted to a float before use (you can do this with float()). That conversions is lossy but it won't matter as long as you are consistent, since Maya is limited to 64 bit precision at most. You can do decimal math for anything you control but the final representation in the maya scene will always be constrained to the 64-bit limit.

    If you are running into precision issues you may have better luck by carefully choosing the correct unit scale in Maya. Maya's internal workings are always in centimeters: the part that says '1 unit = 1 foot' or '1 unit = 1 km' is in the UI, but the conversion can cause precision issues. When in doubt leave the UI in centimeters so there are no extra conversion operations muddying the waters.