Skip to content

Instantly share code, notes, and snippets.

@Eterea
Last active August 6, 2020 15:55
Show Gist options
  • Save Eterea/d59f7264919497d348fd547219bbda34 to your computer and use it in GitHub Desktop.
Save Eterea/d59f7264919497d348fd547219bbda34 to your computer and use it in GitHub Desktop.
#python
# ---------------------------------------------------------------------------------
# NAME: etr_move_nudge_xyz.py
# VERS: 1.4
# DATE: August 5, 2020
#
# MADE: Cristobal Vila, etereaestudios.com
#
# USES: To move nudge (and duplicate, optionally) fixed amounts along XYZ axis
# TODO: Make it more Clean and Robust
# Allow duplicate items (instance, better)
# ---------------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------
#
# FUNCTIONS
#
# ------------------------------------------------------------------------------------------------
# TO KNOW OUR SELECTION MODE
# ------------------------------------------------------------------------------------------------
def 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
mySelMode = selmode()
elements = ['vertex', 'edge', 'polygon', 'item']
components = ['vertex', 'edge', 'polygon']
# DIALOG FOR WHEN NOTHING IS SELECTED AND USER TRY TO DUPLICATE THINGS (BAD IDEA)
# ------------------------------------------------------------------------------------------------
def fn_dialogAdviceDupli():
lx.eval("dialog.setup info")
lx.eval("dialog.title {Eterea Move Nudge - Duplicate}")
lx.eval("dialog.msg {You are trying to move things with DUPLICATE option, but NOTHING is selected!\n\
This is not allowed because bad things could occur (ie: generate co-located geometry)\n\
You need to select something before, to be duplicated using these tools.}")
lx.eval("dialog.open")
sys.exit()
# ------------------------------------------------------------------------------------------------
#
# ARGUMENTS AND USER VALUES
#
# ------------------------------------------------------------------------------------------------
# Query our arguments, 'axis' for XYZ or multiaxis situations and 'sign' for pos +1 / neg -1 direction
axis = lx.args()[0]
sign = int(lx.args()[1])
# Query our external user values
dupli = lx.eval("user.value etr_nudge_move_duplistate ?")
mastat_x = lx.eval("user.value etr_nudge_move_multiaxis_x ?")
mastat_y = lx.eval("user.value etr_nudge_move_multiaxis_y ?")
mastat_z = lx.eval("user.value etr_nudge_move_multiaxis_z ?")
# For this nudge move tool we need to create first of all 3 variables for distX,Y,Z equal to zero
# NOTE: this is necesary because user.values here are 'temporary' (we don't need this with scale & rotate nudges)
distX = 0
distY = 0
distZ = 0
# --------------------------------------------------------------------------------------------------
# BUG: LACK OF PRECISION WITH USER VALUES DEFINED ON A CFG vs DEFINED ON A SCRIPT
#
# If you define a 'userValue' through an external config file (CFG) you will suffer a nasty
# limitation with precision: no more than 2 decimals will show (!)
#
# Instead, defining the lifetime of your user value as 'temporary', inside a script,
# this limitation disappear and you are allowed to use up to 4 decimals.
#
# Example: using a script the value will be printed '67.4955cm'. Using a 'config', it will be '67.5cm'
#
# On the other hand, using a userValue for 'Distance' defined on a CFG you will never
# be able to introduce a really small distance, like 15um. It will round to 0.
#
# For these reasons these user values are defined here, instead of using an external CGF.
#
# DISCUSED HERE:
# https://community.foundry.com/discuss/topic/91732
# --------------------------------------------------------------------------------------------------
# Lets query if our userValues are cr eated or not
etr_move_x_query = lx.eval("query scriptsysservice userValue.isDefined ? etr_nudge_move_x")
etr_move_y_query = lx.eval("query scriptsysservice userValue.isDefined ? etr_nudge_move_y")
etr_move_z_query = lx.eval("query scriptsysservice userValue.isDefined ? etr_nudge_move_z")
# Create or update userValues depending on previous queries
if etr_move_x_query == 0:
lx.eval("user.defNew etr_nudge_move_x distance temporary")
distX = lx.eval("user.value etr_nudge_move_x 0.0")
else:
distX = lx.eval("user.value etr_nudge_move_x ?")
if etr_move_y_query == 0:
lx.eval("user.defNew etr_nudge_move_y distance temporary")
distY = lx.eval("user.value etr_nudge_move_y 0.0")
else:
distY = lx.eval("user.value etr_nudge_move_y ?")
if etr_move_z_query == 0:
lx.eval("user.defNew etr_nudge_move_z distance temporary")
distZ = lx.eval("user.value etr_nudge_move_z 0.0")
else:
distZ = lx.eval("user.value etr_nudge_move_z ?")
# ------------------------------------------------------------------------------------------------
#
# PRELIMINARY STUFF
#
# ------------------------------------------------------------------------------------------------
# FOR WHEN PALETTE IS ALREADY OPEN BUT MOVE IS DISABLED
# ------------------------------------------------------------------------------------------------
# Just in case that nudge form is open but move tool is disabled,
# because user didn't arrived here through launcher script
# NOTE: we do NOT perform the nudge operation on this step and just activate the Move tool
# because the center of movement could be away from the center of selection
if lx.eval("tool.set TransformMove ?") != 'on':
lx.eval("tool.set TransformMove on")
sys.exit()
# NEEDED FOR MULTIAXIS OPERATIONS
# ------------------------------------------------------------------------------------------------
# Create a 'Multi Axis Status' dictionary to be used for negate multi axis move
mastatneg_dict = {
'0': '0',
'1': '2',
'2': '1',
}
# Use that dictionary to negate multiaxis move only if our argument is 'multineg'
if axis == 'multineg':
mastat_x = (mastatneg_dict[mastat_x])
mastat_y = (mastatneg_dict[mastat_y])
mastat_z = (mastatneg_dict[mastat_z])
# NEEDED FOR 'DUPLICATE' OPTION
# ------------------------------------------------------------------------------------------------
# Try to see if a third argument exists (no matter what it's)
# If so, change 'dupli' variable to 1
# This is to allow nudge+duplication using an alternate command, with a key modifier.
# We use 'dupli' as a third argument, but it really does not matter. Could be 'smith' or '42' also.
try:
lx.args()[2]
dupli = 1
except:
pass
# Define a dictionary for different component modes to get plural (needed for next lines)
comp_dict = {
'vertex': 'verts',
'edge': 'edges',
'polygon': 'polys',
}
# Check if something is selected when in component mode. This is important when 'Duplicate' option is enabled
# If nothing is selected in our selection modo, then a 'dupliAlert = 1' is generated, to be used later
dupliAlert = 0
if mySelMode in components:
selComp = lx.evalN("query layerservice %s ? selected" % comp_dict[mySelMode])
if len(selComp) == 0:
dupliAlert = 1
# STEPS IF 'DUPLICATE' IS ENABLED (NO MATTER IF THROUGH 'USER.VALUE' OR BY A THIRD ARGUMENT EXISTENCE)
# ------------------------------------------------------------------------------------------------
if dupli == 1 and mySelMode in components:
# In case that nothing is selected we abort the scritp, launching a dialog with advices
if dupliAlert == 1:
fn_dialogAdviceDupli()
# Query current prefs about copy and paste behaviour
copyDeSelectionState = lx.eval("pref.value application.copyDeSelection ?")
pasteSelectionState = lx.eval("pref.value application.pasteSelection ?")
pasteDeSelectionState = lx.eval("pref.value application.pasteDeSelection ?")
# Change all those Prefs to 'false' temporarily.
lx.eval("pref.value application.copyDeSelection false")
lx.eval("pref.value application.pasteSelection false")
lx.eval("pref.value application.pasteDeSelection false")
# Copy and paste our geometry (our selected one will remain selected, not the pasted)
lx.eval('copy')
lx.eval('paste')
# Return prefs on copy & paste to the original conditions
lx.eval("pref.value application.copyDeSelection %s" % copyDeSelectionState)
lx.eval("pref.value application.pasteSelection %s" % pasteSelectionState)
lx.eval("pref.value application.pasteDeSelection %s" % pasteDeSelectionState)
elif dupli == 1 and mySelMode == 'item': # HERE IS FROM WHERE I'M HAVING THE PROBLEM !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
lx.eval("item.duplicate true locator false true")
# ------------------------------------------------------------------------------------------------
#
# PERFORM THE NUDGE MOVE OPERATIONS
#
# ------------------------------------------------------------------------------------------------
# Apply nudge depending on all of the above
if axis == 'X':
lx.eval("tool.attr xfrm.transform TX %s" % (distX*sign))
elif axis == 'Y':
lx.eval("tool.attr xfrm.transform TY %s" % (distY*sign))
elif axis == 'Z':
lx.eval("tool.attr xfrm.transform TZ %s" % (distZ*sign))
# This is for Multi Axis Move Nudge (valid for both arguments 'multi' and 'multineg')
else:
# We need specific variables here for each axis sign (positive or negative)
# Let's start defining all as positive
signX = 1
signY = 1
signZ = 1
# Our strings '...', 'pos X' and 'neg X' in our 'user.value' multi axis
# give us the strings '0', '1' and '2'
if mastat_x == '0':
distX = 0
elif mastat_x == '1':
signX = 1
else:
signX = -1
if mastat_y == '0':
distY = 0
elif mastat_y == '1':
signY = 1
else:
signY = -1
if mastat_z == '0':
distZ = 0
elif mastat_z == '1':
signZ = 1
else:
signZ = -1
lx.eval("tool.attr xfrm.transform TX %s" % (distX*signX))
lx.eval("tool.attr xfrm.transform TY %s" % (distY*signY))
lx.eval("tool.attr xfrm.transform TZ %s" % (distZ*signZ))
lx.eval("tool.doApply")
# These last off / on commands are to re-center the handles around selection after each nudge step
lx.eval("tool.set TransformMove off")
lx.eval("tool.set TransformMove on")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment