Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
MODO-Python hack to solve the problem when your handles for Move, Scale, Rotate, appears giant or dwarf when seeing from your Camera.
#python
# ------------------------------------------------------------------------------------------------
# NAME: etr_fixHandlesFromCamera.py
# VERS: 1.0
# DATE: December, 27 - 2016
#
# MADE: Cristobal Vila, etereaestudios.com
#
# USES: This is hack to solve the problem when your handles for Move, Scale, Rotate,
# appears giant or dwarf when looking through your Camera.
#
# It calculates the distance from Camera to the center of selection and use this value to
# change the Target Distance on Camera, automatically.
#
# For the moment it only works for Components (TO DO: work with any selected Item/s)
# ------------------------------------------------------------------------------------------------
import lx
import math
# //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
# FUNCTIONS
#
# //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# Query selection mode
def fn_selmode(*types):
if not types:
types = ('vertex', 'edge', 'polygon', 'item', 'pivot', 'center', 'ptag')
for t in types:
if lx.eval('select.typeFrom %s;vertex;edge;polygon;item;pivot;center;ptag ?' %t):
return t
# Dialog with advices in case of wrong selection mode or nothing selected
def fn_dialogAdvice():
lx.eval('dialog.setup info')
lx.eval('dialog.title {Eterea Fix Handles From Camera}')
lx.eval('dialog.msg {Use when your handle appears giant or dwarf when looking through your Camera.\n\
\n\
For the moment you can only work in Vertex, Edge or Poly Mode.\n\
Works with these handles: Move, Rotate, Axis Rotate, Scale, Uniform Scale and Transform.}')
lx.eval('dialog.open')
sys.exit()
# //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
# PRELIMINARY STUFF
#
# //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# If not in edge or poly mode, launch dialog with advices
mySelMode = fn_selmode()
allowedModes = ['vertex', 'edge', 'polygon']
if mySelMode not in allowedModes:
fn_dialogAdvice()
# If nothing is selected, launch dialog with advices
dic_comp = {'edge': 'edges', 'polygon': 'polys'}
components = lx.evalN('query layerservice %s ? selected' % dic_comp[mySelMode])
if len(components) == 0:
fn_dialogAdvice()
# Check which transform handle is active (if any) to recover it at end. It must be an easier and direct method for this...
myActiveTool = []
if lx.eval('tool.set TransformMove ?') == 'on':
myActiveTool = 'TransformMove'
elif lx.eval('tool.set TransformScale ?') == 'on':
myActiveTool = 'TransformScale'
elif lx.eval('tool.set TransformUScale ?') == 'on':
myActiveTool = 'TransformUScale'
elif lx.eval('tool.set TransformRotate ?') == 'on':
myActiveTool = 'TransformRotate'
elif lx.eval('tool.set Transform ?') == 'on':
myActiveTool = 'Transform'
elif lx.eval('tool.set xfrm.rotate ?') == 'on':
myActiveTool = 'xfrm.rotate'
else:
fn_dialogAdvice()
# Query Current Layer and be sure to select it on Item List. Query also the meshItem, to return back later
currentLayer = lx.eval1('query layerservice layer.index ? main')
mainFGlayer = lx.eval1('query layerservice layer.id ? fg')
lx.eval('select.subItem {%s} set mesh;locator' % mainFGlayer)
myCurrentMesh = lx.eval('query sceneservice selection ? mesh')
# //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
# CORE
#
# //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# Query Bounding Box of selected components using external script 'calculate_bbox.py' by Farfarer
# http://community.thefoundry.co.uk/discussion/post.aspx?f=119&t=93824&p=839842
boundBox = lx.evalN('ffr.getbbox ? world') # Get world space boundBox as (+X, +Y, +Z, -X, -Y, -Z)
# Calculate average (arithmetic mean) of resulting coords to get the Center for that Bounding Box
# http://stackoverflow.com/questions/7716331/calculating-arithmetic-mean-average-in-python
def average(numbers):
return float(sum(numbers)) / max(len(numbers), 1)
avgX = average([boundBox[0], boundBox[3]])
avgY = average([boundBox[1], boundBox[4]])
avgZ = average([boundBox[2], boundBox[5]])
myTargetPos = (avgX, avgY, avgZ) # This is the average/center position for selected components
lx.out('myTargetPos:', myTargetPos)
# Calculate world position for last active Camera selected in our 3D viewport (if any)
myCam = lx.eval('view3d.cameraItem ?')
if myCam:
myCamPos = lx.eval('query sceneservice camera.worldPos ? %s' % myCam)
lx.out('myCamPos:', myCamPos)
else:
fn_dialogAdvice()
# Calculate distance from Camera to selected stuff using Pythagoras Theorem
ABX = myTargetPos[0] - myCamPos[0]
ABY = myTargetPos[1] - myCamPos[1]
ABZ = myTargetPos[2] - myCamPos[2]
AB_distance = math.sqrt(ABX**2 + ABY**2 + ABZ**2) #Pythagoras Theorem
lx.out('Distance:', AB_distance)
myTarget = AB_distance*0.35 # Direct experience reveals that using 'distance*0.35' gives a more balanced size for handles
# Apply this value to our Camera Target Distance
lx.eval('select.item %s' % myCam)
lx.eval('query layerservice layer.index ? selected')
# Since it's very probable that our camera is locked, check it first
lockStatus = lx.eval('item.channel locator$lock ?')
# If not locked for sure ('off') then we can proceed to change target distance
if lockStatus == 'off':
lx.eval('item.channel camera$target %s' % myTarget)
# If locked for sure ('on') or 'maybe' ('default'), change temporarily to 'off', apply myTarget and recover previous state
else:
lx.eval('item.channel locator$lock off')
lx.eval('item.channel camera$target %s' % myTarget)
lx.eval('item.channel locator$lock %s' % lockStatus)
# Return back to our original mesh item, selection mode and active tool
lx.eval('select.item %s set' % myCurrentMesh)
lx.eval('query layerservice layer.index ? selected')
lx.eval('select.typeFrom %s' % mySelMode)
lx.eval('tool.set %s on' % myActiveTool)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment