Last active
August 18, 2020 18:15
-
-
Save Eterea/a028ce392a8fd41f46922d505406614f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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