Skip to content

Instantly share code, notes, and snippets.

@Eterea
Created August 31, 2022 15:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Eterea/9dfe68418e3a469531291d2e0fdb045f to your computer and use it in GitHub Desktop.
Save Eterea/9dfe68418e3a469531291d2e0fdb045f to your computer and use it in GitHub Desktop.
#python
# ------------------------------------------------------------------------------------------------
# NAME: etr_python_api_power_utils.py
# VERS: 1.5
# DATE: August 24, 2020
#
# MADE: Cristobal Vila, etereaestudios.com
# With snippets kindly shared by Shawn Frueh, shawnfrueh.com/me/ and also 'robberyman'#
# ------------------------------------------------------------------------------------------------
import lx
import lxu
import math
import modo
"""
------------------------------------------------------------------------------------------------
SUPER VERTS POSITION CALCULATOR
------------------------------------------------------------------------------------------------
Next pure Python API snippet kindly shared by Shawn Frueh:
https://foundry-modo.slack.com/archives/C6F918JEB/p1596930293212700
THIS IS WHAT REALLY MAKES THESE TOOLS SO POWERFULL. A BIG THANKS TO SHAWN FOR THIS!
------------------------------------------------------------------------------------------------
Here is a pure API method that uses the selection service to get all selected points on any object in Modo.
This will include points that are selected even in ghost mode of the procedural stack.
When getting the selection mesh, it should be the "deformed" version that you are seeing in the view-port,
thus working with procedurals. If you want to also handle global positions you will have to multiply the point
by it's items world matix. I hope this helps! Tried to break it down as simple as possible.
I does look like a lot of code but the joy is once you wrap this bad boy up into a package/library
that you just import you never have to worry about it again. That's essentially where this came from.
I have a method that's a bit more advanced than this that also handles the other elements.
------------------------------------------------------------------------------------------------
TIP BY JAMES O'HARE AKA FARFARER /////////////////////////
If you get a selection of verts indirectly (ie: selecting edges and converting to verts)
you could arrive to a situation with 'extra verts' selected, this is: discountinuous (disco) verts
Vertex selections are technically made up of a vertex/polygon pair
in that is the vertex ID and the polygon ID that it belongs to
(in the case where the polygon ID is 0/NULL, it's implied that the vertex ID selected
for all of its polygons - a continuous selection)
SUMMARIZING:
If you want the raw vertex selection count, regardless of discontinuity,
you'll want to ITERATE OVER THE SELECTIONS AND COUNT THE NUMBER OF UNIQUE VERTEX-IDs LISTED
In this function we really don't want the 'raw vert count', but applying this scheme we avoid the
extra calculation of positions for those extra disco verts.
"""
def fn_get_sel_verts_pos(world=False):
# Args: world (bool): If true, return the world positions, else return local
# Returns: list(Vector3): List with XYZ positions for each selected vert
# Initialize the selection service
SELECTION_SERVICE = lxu.service.Selection()
# Get the vertex selection int type: 1447383640
vertex_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_VERTEX)
# Get the selection object: lxu.object.SelectionType
vertex_selection_object = SELECTION_SERVICE.Allocate(lx.symbol.sSELTYP_VERTEX)
# Convert that type into a packet so that we can access the selection data
vertex_packet = lx.object.VertexPacketTranslation(vertex_selection_object)
# Get the total count of selected points
selected_vertex_count = SELECTION_SERVICE.Count(vertex_selection_type)
vertex_id_list = [] # READ TIP BY JAMES O'HARE AKA FARFARER /////////////////////////
position_list = []
# Iterate over each point and get some data. These points could be coming from multiple meshes
# and so you will need to check the item they be
for vertex in range(selected_vertex_count):
# Get a pointer to the vertex data in the given index.
vertex_pointer = SELECTION_SERVICE.ByIndex(vertex_selection_type, vertex)
# If we don't get a pointer, skip to the next index in the loop.
# This is necessary to prevent any crashes.
if not vertex_pointer:
continue
# Get the id data from the pointer: ex. 495128304
vertex_id = vertex_packet.Vertex(vertex_pointer)
if vertex_id not in vertex_id_list: # /////////////////////////
vertex_id_list.append(vertex_id) # /////////////////////////
# Get the item the point belongs to: lxu.object.Item
selection_item = vertex_packet.Item(vertex_pointer)
# Get the matrix channel index
matrix_index = selection_item.ChannelLookup("worldMatrix")
# Prep the selection item to be read at the current time. You want those animated points!
selection_item.ReadEvaluated(SELECTION_SERVICE.GetTime())
# Get the matrix from the channel as a COM object and convert it to a matrix object.
item_world_matrix = lx.object.Matrix(selection_item.ChannelValue(matrix_index))
# Get the mesh item the point belongs to: lxu.object.Mesh
selection_mesh = vertex_packet.Mesh(vertex_pointer)
# Get the point accessor of the mesh item: lxu.object.Point
point_item = selection_mesh.PointAccessor()
# Select the vertex in the accessor so that we can query any Point data.
point_item.Select(vertex_id)
# Get the position of the internally selected point. This point is relative to the mesh
# So if the mesh is not zero you will need to multiply this position by the transform
# data of the item it belongs to.
localPosition = point_item.Pos()
# Get the world position of the point by multiplying it by the world matrix of it's item.
worldPosition = item_world_matrix.MultiplyVector(point_item.Pos())
# Deliver world or local depending on world argument ('true', 'false')
if world:
position_list.append(worldPosition)
else:
position_list.append(localPosition)
return position_list
# ------------------------------------------------------------------------------------------------
# SUPER VERTS COUNT CALCULATOR
# To avoid problem with 'disco verts' Read TIP BY JAMES O'HARE AKA FARFARER
# ------------------------------------------------------------------------------------------------
def fn_get_sel_verts_count():
# Initialize the selection service
SELECTION_SERVICE = lxu.service.Selection()
# Get the vertex selection int type: 1447383640
vertex_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_VERTEX)
# Get the selection object: lxu.object.SelectionType
vertex_selection_object = SELECTION_SERVICE.Allocate(lx.symbol.sSELTYP_VERTEX)
# Convert that type into a packet so that we can access the selection data
vertex_packet = lx.object.VertexPacketTranslation(vertex_selection_object)
# Get the total count of selected points
selected_vertex_count = SELECTION_SERVICE.Count(vertex_selection_type)
vertex_id_list = [] # READ TIP BY JAMES O'HARE AKA FARFARER /////////////////////////
# Iterate over each point and get some data. These points could be coming from multiple meshes
# and so you will need to check the item they be
for vertex in range(selected_vertex_count):
# Get a pointer to the vertex data in the given index.
vertex_pointer = SELECTION_SERVICE.ByIndex(vertex_selection_type, vertex)
# If we don't get a pointer, skip to the next index in the loop.
# This is necessary to prevent any crashes.
if not vertex_pointer:
continue
# Get the id data from the pointer: ex. 495128304
vertex_id = vertex_packet.Vertex(vertex_pointer)
if vertex_id not in vertex_id_list: # /////////////////////////
vertex_id_list.append(vertex_id) # /////////////////////////
return len(vertex_id_list)
"""
------------------------------------------------------------------------------------------------
SUPER EDGES LENGTH CALCULATOR
------------------------------------------------------------------------------------------------
Next pure Python API snippet is a mix between the one shared by Shawn Frueh:
https://foundry-modo.slack.com/archives/C6F918JEB/p1596930293212700
And also this one shared by 'robberyman':
https://foundry-modo.slack.com/archives/C6F918JEB/p1597076342264900
With this we can calculate the total accumulated length for all selected edges, no matter
these ones belong to different item meshes, procedural, deformed, animated... ALL!
"""
def fn_get_sel_edges_length():
total_distance = 0
# Initialize the selection service
SELECTION_SERVICE = lxu.service.Selection()
# Get the edge selection int type
edge_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_EDGE)
# Get the selection object: lxu.object.SelectionType
edge_selection_object = SELECTION_SERVICE.Allocate(lx.symbol.sSELTYP_EDGE)
# Convert that type into a packet so that we can access the selection data
edge_translation_packet = lx.object.EdgePacketTranslation(edge_selection_object)
# Get the total count of selected points
selected_edge_count = SELECTION_SERVICE.Count(edge_selection_type)
my_edges_distances_list = []
# Iterate over each edge and get some data. These edges could be coming from multiple meshes
# and so you will need to check the item they be
for edge_index in range(selected_edge_count):
# Get a pointer to the vertex data in the given index.
edge_pointer = SELECTION_SERVICE.ByIndex(edge_selection_type, edge_index)
# If we don't get a pointer, skip to the next index in the loop.
# This is necessary to prevent any crashes.
if not edge_pointer:
continue
# Get the item the edge belongs to: lxu.object.Item
selection_item = edge_translation_packet.Item(edge_pointer)
item = lx.object.Item(selection_item)
# Get the matrix channel index
matrix_index = selection_item.ChannelLookup("worldMatrix")
# Prep the selection item to be read at the current time. You want those animated points!
selection_item.ReadEvaluated(SELECTION_SERVICE.GetTime())
# Get the matrix from the channel as a COM object and convert it to a matrix object.
item_world_matrix = lx.object.Matrix(selection_item.ChannelValue(matrix_index))
# Get the mesh item the edge belongs to: lxu.object.Mesh
selection_mesh = edge_translation_packet.Mesh(edge_pointer)
mesh = lx.object.Mesh(selection_mesh)
# IDs for the edge endpoints,
aPoint, bPoint = edge_translation_packet.Vertices(edge_pointer)
point = mesh.PointAccessor()
# Select and get the position of boths points. This will be relative to the mesh
# So if the mesh is not zero you will need to multiply this position by the transform data
# of the item it belongs to. We do this by multiplying it by the world matrix of it's item.
point.Select(aPoint)
posA = item_world_matrix.MultiplyVector(point.Pos())
point.Select(bPoint)
posB = item_world_matrix.MultiplyVector(point.Pos())
# Now lets calculate the individual X, Y, Z distances between both points
ABX = posA[0] - posB[0]
ABY = posA[1] - posB[1]
ABZ = posA[2] - posB[2]
AB_distance = math.sqrt(ABX**2 + ABY**2 + ABZ**2) # Pythagoras Theorem
my_edges_distances_list.append(AB_distance) # Append distance(s) to list
return my_edges_distances_list
# ------------------------------------------------------------------------------------------------
# SUPER EDGES COUNT CALCULATOR
# ------------------------------------------------------------------------------------------------
def fn_get_sel_edges_count():
# Initialize the selection service
SELECTION_SERVICE = lxu.service.Selection()
# Get the edge selection int type
edge_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_EDGE)
# Get the total count of selected points
selected_edge_count = SELECTION_SERVICE.Count(edge_selection_type)
return selected_edge_count
# ------------------------------------------------------------------------------------------------
# SUPER POLYS COUNT CALCULATOR
# ------------------------------------------------------------------------------------------------
def fn_get_sel_polys_count():
# Initialize the selection service
SELECTION_SERVICE = lxu.service.Selection()
# Get the polygon selection int type: 1347374169
polygon_selection_type = SELECTION_SERVICE.LookupType(lx.symbol.sSELTYP_POLYGON)
# Get the total count of selected polygons
selected_poly_count = SELECTION_SERVICE.Count(polygon_selection_type)
return selected_poly_count
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment