Skip to content

Instantly share code, notes, and snippets.

@Eterea
Last active August 18, 2020 18:15
Show Gist options
  • Save Eterea/a028ce392a8fd41f46922d505406614f to your computer and use it in GitHub Desktop.
Save Eterea/a028ce392a8fd41f46922d505406614f to your computer and use it in GitHub Desktop.
#python
# ---------------------------------------------------------------------------------------------------
# NAME: etr_match_posrot_smart.py
# VERS: 1.0
# DATE: August 18, 2020
#
# MADE: Cristobal Vila, etereaestudios.com
#
# USES: To move and rotate a source mesh item to match another target mesh item
# 1. Select 3 verts in order in your SOURCE mesh item, following a 'L' form
# 2. Select 3 verts in order in your TARGET mesh item, following a 'L' form
# 3. Call this script
#
# ARGS: @etr_match_posrot_smart.py relative
# @etr_match_posrot_smart.py targeted
# @etr_match_posrot_smart.py original
# ---------------------------------------------------------------------------------------------------
import lxu, lx
# ARGUMENT
# ---------------------------------------------------------------------------------------------------
# 'relative': center travel with source, maintaining its relative pos & rot
# 'targeted': center for source travel to match target center pos & rot
# 'original': center for source remains fixed in original place
sourCenter_end = lx.arg()
# ---------------------------------------------------------------------------------------------------
#
# FUNCTIONS
#
# ---------------------------------------------------------------------------------------------------
# Determine selection type
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
sel_mode = selmode()
# Dialog for when user is in wrong mode or nothing is selected. And some advices
# ------------------------------------------------------------------------------------------------
def fn_dialogAdvice():
lx.eval("dialog.setup info")
lx.eval("dialog.title {Eterea Match Source to Target Pos Rot}")
lx.eval("dialog.msg {To move and rotate a source mesh item to match another target mesh item.\n\
- Select 3 verts in order in your SOURCE mesh item, following a 'L' form\n\
- Select 3 verts in order in your TARGET mesh item, following a 'L' form\n\
- Press this button.}")
lx.eval("dialog.open")
lx.eval("user.value etr_gi_selected (none)")
sys.exit()
if sel_mode != 'vertex':
fn_dialogAdvice()
"""
Funcion to avoid the need to select first your source mesh and then your target one in the item list.
Thanks to this, the script will be smart enough to know that the 3 first selected verts belongs to the
Source Mesh, and the last 3 selected ones belongs to the Target Mesh, no matter the original order
in your selection of meshes in the item list.
---------------------------------------------------------------------------------------------------
This Python API function was shared by James O'Hare aka Farfarer on this Slack thread:
https://foundry-modo.slack.com/archives/C6F918JEB/p1597655650070900
Shared also here:
https://community.foundry.com/discuss/post/1205786
Using James words:
So with this you can just put it into your file and call the function.
It will return two lists - one with item idents, one with vertex indices for that item.
And then you can do with those as you like - the first entry in each list will be the "primary" item/verts.
And each subsequent pairs of entries are for each other mesh, in order of selection.
"""
def infoForMatching():
item_order = []
vert_order = {}
sel_svc = lx.service.Selection ()
sel_type_vertex = sel_svc.LookupType (lx.symbol.sSELTYP_VERTEX)
vertex_pkt_trans = lx.object.VertexPacketTranslation (sel_svc.Allocate (lx.symbol.sSELTYP_VERTEX))
for vertex_sel_idx in xrange (sel_svc.Count (sel_type_vertex)):
pkt = sel_svc.ByIndex (sel_type_vertex, vertex_sel_idx)
item = vertex_pkt_trans.Item (pkt)
item_ident = item.Ident()
if item_ident not in item_order:
item_order.append(item_ident)
vertex = vertex_pkt_trans.Vertex(pkt)
mesh = vertex_pkt_trans.Mesh(pkt)
point = lx.object.Point(mesh.PointAccessor())
point.Select(vertex)
vertex_index = int(point.Index())
if item_ident not in vert_order:
vert_order[item_ident] = [vertex_index,]
elif len(vert_order[item_ident]) != 3 and vertex_index not in vert_order[item_ident]:
vert_order[item_ident].append(vertex_index)
num_items = len(item_order)
if num_items < 2:
return ((), ())
primary_item = item_order[0]
if len(vert_order[primary_item]) != 3:
return ((), ())
output_items = [primary_item,]
output_verts = [tuple(vert_order[primary_item]),]
for item_index in xrange(1, num_items):
secondary_item = item_order[item_index]
if len(vert_order[secondary_item]) != 3:
continue
output_items.append(secondary_item)
output_verts.append(tuple(vert_order[secondary_item]))
if len(output_items) < 2:
return ((), ()) # must have at least 2 items to match
return (output_items, output_verts)
# Fit workplane to selection and match center owner to wp position and rotation
# ------------------------------------------------------------------------------------------------
def fn_center_to_workplane(center_owner):
lx.eval("workPlane.fitSelect") # Fit workplane to selection
lx.eval("select.center {%s}" % center_owner) # Select center for that item mesh
lx.eval("center.matchWorkplanePos") # Match center to workplane Position
lx.eval("center.matchWorkplaneRot") # Match center to workplane Rotation
# ---------------------------------------------------------------------------------------------------
#
# Preliminary stuff
#
# ---------------------------------------------------------------------------------------------------
items, verts = infoForMatching()
if len(items) != 2:
fn_dialogAdvice()
source_layer_ID = items[0]
target_layer_ID = items[1]
source_layer_index = lx.eval1("query layerservice layer.index ? %s" % source_layer_ID)
target_layer_index = lx.eval1("query layerservice layer.index ? %s" % target_layer_ID)
source_verts = verts[0]
target_verts = verts[1]
if len(source_verts) != 3 or len(target_verts) != 3:
fn_dialogAdvice()
# Althoug user select verts following an easy 'L' order, we need a different selection order
# First the one corresponding to origin for our workplane
# Second the one that determined the X axis
# Third the one to determine the XZ plane with others
verts_ID_sorted = [1,2,0]
# ---------------------------------------------------------------------------------------------------
#
# Re-locate the SOURCE Mesh Center using our verts (but also storing original center placement)
#
# ---------------------------------------------------------------------------------------------------
if sourCenter_end == 'relative' or sourCenter_end == 'original':
# First we will store the Source Item Center using an intermediate temporal locator
lx.eval("select.drop item") # Deselect items
lx.eval("select.subItem %s set" % source_layer_ID) # Select the SOURCE item mesh
lx.eval("workPlane.fitSelect") # Fit workplane to our Source Mesh Item
lx.eval("item.create locator") # Create locator
source_loc_store = lx.eval("query sceneservice selection ? locator") # Query it for further calls
# Match workplane position and rotation
lx.eval('matchWorkplanePos')
lx.eval('matchWorkplaneRot')
if sourCenter_end == 'relative':
lx.eval("select.subItem %s add" % source_layer_ID) # Add source item mesh to selection
lx.eval("item.parent inPlace:1") # Parent in place (locator will be child of source mesh)
lx.eval("select.drop item") # Deselect all items
lx.eval("select.subItem %s set" % source_layer_ID) # Select the SOURCE item mesh
# Deselect all verts selection (this will change also the selection type to 'verts')
lx.eval("select.drop vertex")
# Re-select our 3 SOURCE verts in a convenient 1-2-0 order
for vert_ID in verts_ID_sorted:
lx.eval("select.element %s vertex add %s" % (source_layer_index , source_verts[vert_ID]))
# Fit workplane to selection and match center for source_layer_ID to wp pos & rot
fn_center_to_workplane(source_layer_ID)
# ---------------------------------------------------------------------------------------------------
#
# Re-locate the TARGET Mesh Center using our verts (but also storing original center placement)
#
# ---------------------------------------------------------------------------------------------------
# First we will store the Target Item Center using using another intermediate temporal locator
lx.eval("select.drop item") # Deselect items
lx.eval("select.subItem %s set" % target_layer_ID) # Select the TARGET item mesh
lx.eval("workPlane.fitSelect") # Fit workplane to our Target Mesh Item
lx.eval('item.create locator') # Create locator
target_loc_store = lx.eval("query sceneservice selection ? locator") # Query it for further calls
# Match workplane position and rotation
lx.eval('matchWorkplanePos')
lx.eval('matchWorkplaneRot')
lx.eval("select.drop item") # Deselect items
lx.eval("select.subItem %s set" % target_layer_ID) # Select the TARGET item mesh
# Deselect all verts selection (this will change also the selection type to 'verts')
lx.eval("select.drop vertex")
# Re-select our 3 TARGET verts in a convenient 1-2-0 order
for vert_ID in verts_ID_sorted:
lx.eval("select.element %s vertex add %s" % (target_layer_index , target_verts[vert_ID]))
# Fit workplane to selection and match center for target_layer_ID to wp pos & rot
fn_center_to_workplane(target_layer_ID)
# ---------------------------------------------------------------------------------------------------
#
# Match Position and Rotation between both Source and Target meshes
#
# ---------------------------------------------------------------------------------------------------
# Deselect items
lx.eval("select.drop item")
# Reselect again both items in order (first the Source, then the Target)
lx.eval("select.subItem %s add" % source_layer_ID)
lx.eval("select.subItem %s add" % target_layer_ID)
# Match both Position and Rotation
lx.eval('matchPos')
lx.eval('matchRot')
# ---------------------------------------------------------------------------------------------------
#
# Recover original center for Source
#
# ---------------------------------------------------------------------------------------------------
lx.eval("select.drop item") # Deselect items
if sourCenter_end == 'relative' or sourCenter_end == 'original':
lx.eval("select.subItem %s set" % source_loc_store) # Select our source locator store
if sourCenter_end == 'relative':
lx.eval("item.parent parent:{} inPlace:1") # Unparent in place
# Fit workplane to selection and match center for source_layer_ID to wp pos & rot
fn_center_to_workplane(source_layer_ID)
lx.eval("select.subItem %s set" % source_loc_store) # Select our source locator store
lx.eval("item.delete") # Delete source locator store
elif sourCenter_end == 'targeted':
lx.eval("select.subItem %s set" % target_loc_store) # Select our target locator store
# Fit workplane to selection and match center for source_layer_ID to wp pos & rot
fn_center_to_workplane(source_layer_ID)
# ---------------------------------------------------------------------------------------------------
#
# Recover original center for Target
#
# ---------------------------------------------------------------------------------------------------
lx.eval("select.drop item") # Deselect items
lx.eval("select.subItem %s set" % target_loc_store) # Select our target locator store
# Fit workplane to selection and match center for target_layer_ID to wp pos & rot
fn_center_to_workplane(target_layer_ID)
lx.eval("select.subItem %s set" % target_loc_store) # Select our target locator store
lx.eval("item.delete") # Delete target locator store
# ---------------------------------------------------------------------------------------------------
#
# Return to original conditions and finish
#
# ---------------------------------------------------------------------------------------------------
# Reset our workplane (will go to origin)
lx.eval("workPlane.reset")
# Add the both meshes to selection
lx.eval("select.subItem %s set" % source_layer_ID)
lx.eval("select.subItem %s add" % target_layer_ID)
# Change to vert selection
lx.eval("select.type %s" % sel_mode)
# Recover our original verts selection for both meshes
for vert in source_verts:
lx.eval("select.element %s vertex add %s" % (source_layer_index, vert))
for vert in target_verts:
lx.eval("select.element %s vertex add %s" % (target_layer_index, vert))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment