Skip to content

Instantly share code, notes, and snippets.

@Eterea
Last active August 29, 2015 13:59
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/10486049 to your computer and use it in GitHub Desktop.
Save Eterea/10486049 to your computer and use it in GitHub Desktop.
Modo script to paste geometry over other “elements” (vertices, edges, polys or items)
#python
# ---------------------------------------------------------------------------------------------------
# NAME: etr_pasteOnElements.py
# VERS: 1.0
# DATE: July 19, 2014
#
# MADE: Cristobal Vila, etereaestudios.com
#
# USES: To paste and orient geometry on a bunch of selected COMPONENTS (Vert, Edges, Polys) or ITEMS
#
# Use '@etr_pasteOnElements.py center' for Bounding Box CENTER alignment
# Use '@etr_pasteOnElements.py base' for Bounding Box BASE alignment
# Use '@etr_pasteOnElements.py top' for Bounding Box TOP alignment
#
# Copy your “prototype” mesh to align it at selected COMPONENTS or ITEMS
# - If you select COMPONENTS or ONLY ONE ITEM-MESH, the pasted geo will be on that ItemMesh
# - If you select VARIOUS ITEMS, the pasted geo will be on a new ItemMesh
#
# The script uses an enhanced method to align our provisional Workplane to selected geo, depending on:
# — Stray vertices: using the 'Y' direction
# — Vertices belonging to poly surfaces or splines: using the local normal
# — Edges: using the local normal
# — Quad Polys: custom enhanced WP aligment
# — Non-quad Polys: default WP alignment
#
# You can differenciate also between 'EACH' or 'GROUP' alignments:
# - EACH means that one copy for each component/item will be placed
# - GROUP means that only one copy will be placed following the WP alignment to the selected group
# ---------------------------------------------------------------------------------------------------
# Defining arguments
place_mode = lx.args()[0] # Choose between 'each' or 'group'
align_mode = lx.args()[1] # Choose between 'center', 'base' or 'top'
# Query 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']
# Function to convert Rad to Deg since "lx.setOption( 'queryAnglesAs', 'degrees' )" doesn't works for all situations (?)
def rad(a):
return a * 57.2957795130
# ---------------------------------------------------------------------------------------
# Query various CUSTOM STATES to return back to originals at end, securely:
# PRS values for our Mesh Item, Copy/Paste Prefs, Symmetry,
# Store Selected Components, Workplane Position and Rotation.
# ---------------------------------------------------------------------------------------
# Query Current Active Scene
lx.eval('query sceneservice scene.name ? main')
if mySelMode in components:
myCurrentMesh = lx.eval('query sceneservice selection ? mesh')
myCurrentLayerID = lx.eval('query layerservice layer.index ? main')
# Abort with message in case that no Mesh is selected on Item List (only for 'Components')
if myCurrentMesh == None:
lx.eval('dialog.setup info')
lx.eval('dialog.title {Eterea Paste on Elements}')
lx.eval('dialog.msg {You are selecting Vertices, Edges or Polys.\n\
Be sure to Select your Current Mesh on Item List}')
lx.eval('dialog.open')
sys.exit()
# Query Pos, Rot and Scale for our Item (since Paste Tool doesn't works fine with PRS-modified MeshItems)
myCM_P = lx.eval('query sceneservice item.pos ? %s' % myCurrentMesh)
myCM_R = lx.eval('query sceneservice item.rot ? %s' % myCurrentMesh)
myCM_S = lx.eval('query sceneservice item.scale ? %s' % myCurrentMesh)
# If our ItemsMesh has been PRS-modified in some way: Reset All (we will recover these values later)
if any([myCM_P[0], myCM_P[1], myCM_P[2], myCM_R[0], myCM_R[1], myCM_R[2]]) or myCM_S[0] != 1 or myCM_S[1] != 1 or myCM_S[2] != 1:
lx.eval('transform.reset all')
myCM_PRS = True
# Do nothing if all is PRS-default
else:
myCM_PRS = False
# Query some prefs and states. Save Selection Set
copyDeSelectionState = lx.eval('pref.value application.copyDeSelection ?')
pasteSelectionState = lx.eval('pref.value application.pasteSelection ?')
symmetryState = lx.eval('select.symmetryState ?')
lx.eval('select.editSet tempSetA add')
# Change Prefs to 'true' temporarily. Disable Symmetry.
lx.eval('pref.value application.copyDeSelection true')
lx.eval('pref.value application.pasteSelection true')
lx.eval('select.symmetryState none')
# Get position and rotation for our Workplane, and Reset it
lx.setOption( 'queryAnglesAs', 'degrees' )
wp_cenX = lx.eval('workPlane.edit cenX:?')
wp_cenY = lx.eval('workPlane.edit cenY:?')
wp_cenZ = lx.eval('workPlane.edit cenZ:?')
wp_rotX = lx.eval('workPlane.edit rotX:?')
wp_rotY = lx.eval('workPlane.edit rotY:?')
wp_rotZ = lx.eval('workPlane.edit rotZ:?')
lx.eval('workPlane.reset')
# -------------------------------------------------------------------------------------
# Function to recover our original ItemMesh PRS values (if modified)
# -------------------------------------------------------------------------------------
def returnItemTo_originalPRS():
lx.eval("transform.channel pos.X %s" % myCM_P[0])
lx.eval("transform.channel pos.Y %s" % myCM_P[1])
lx.eval("transform.channel pos.Z %s" % myCM_P[2])
lx.eval("transform.channel rot.X %s" % rad(myCM_R[0])) # rad() function to convert Radians to Degrees
lx.eval("transform.channel rot.Y %s" % rad(myCM_R[1]))
lx.eval("transform.channel rot.Z %s" % rad(myCM_R[2]))
lx.eval("transform.channel scl.X %s" % myCM_S[0])
lx.eval("transform.channel scl.Y %s" % myCM_S[1])
lx.eval("transform.channel scl.Z %s" % myCM_S[2])
lx.eval('select.typeFrom %s' % mySelMode)
# ---------------------------------------------------------------------------------------
# Function to return back to our original states
# ---------------------------------------------------------------------------------------
def returnTo_originalStates():
lx.eval('pref.value application.copyDeSelection %s' % copyDeSelectionState)
lx.eval('pref.value application.pasteSelection %s' % pasteSelectionState)
lx.eval('select.symmetryState %s' % symmetryState)
lx.eval('select.typeFrom %s' % mySelMode)
if mySelMode == 'item':
lx.eval('select.drop item')
lx.eval('select.useSet tempSetA select') # I prefer to re-select the original elements, at end
# -------------------------------------------------------------------------------------
# Recover our original ItemMesh PRS values (if modified)
# -------------------------------------------------------------------------------------
if mySelMode in components and myCM_PRS == True:
lx.eval("select.item %s" % myCurrentMesh)
returnItemTo_originalPRS()
elif mySelMode == 'item' and itemSelected_N == 1 and itemSelected_Type == 'mesh' and myCM_PRS == True:
lx.eval("select.item %s" % itemSelected_ID)
returnItemTo_originalPRS()
lx.eval('!!select.deleteSet tempSetA') # Delete Selection Set
# -------------------------------------------------------------------------------
# Recover our original Workplane
# -------------------------------------------------------------------------------
# This first part is for when our workplane was 'dynamic' or 'free', to avoid ending with a workplane aligned to 0,0,0 and NOT dynamic.
if abs(wp_cenX) == 0.0 and abs(wp_cenY) == 0.0 and abs(wp_cenZ) == 0.0 and abs(wp_rotX) == 0.0 and abs(wp_rotY) == 0.0 and abs(wp_rotZ) == 0.0:
lx.eval('workPlane.reset')
# This second part is for when our workplane was aligned to any selection.
else:
lx.eval('workPlane.fitSelect')
lx.eval('workPlane.edit %s %s %s %s %s %s' % (wp_cenX, wp_cenY, wp_cenZ, wp_rotX, wp_rotY, wp_rotZ))
# Clear any existing Action Center and Fallofs (because query these guys to recover at end is a pain...)
lx.eval('tool.clearTask axis')
lx.eval('tool.clearTask falloff')
# Query the Index and Number of selected Elements (to use later)
if mySelMode in components:
vertSelected_ID = lx.evalN('query layerservice verts ? selected')
vertSelected_N = lx.eval('query layerservice vert.N ? selected')
edgeSelected_ID = lx.evalN('query layerservice edges ? selected')
edgeSelected_N = lx.eval('query layerservice edge.N ? selected')
polySelected_ID = lx.evalN('query layerservice polys ? selected')
polySelected_N = lx.eval('query layerservice poly.N ? selected')
elif mySelMode == 'item':
itemSelected_ID = lx.evalN('query sceneservice selection ? locator')
itemSelected_N = len(lx.evalN("query sceneservice selection ? locator")) # Didn't found another method for this...
if itemSelected_N == 1:
itemSelected_Type = lx.eval('query sceneservice item.type ? %s' % itemSelected_ID)
# --------------------------------------------------------------------------------------------------------
# Functions for Dialogs notifying about 'none' -OR- slowness using a high number of Elements (For +100)
# --------------------------------------------------------------------------------------------------------
def noElementsSelected_dialog():
lx.eval('dialog.setup info')
lx.eval('dialog.title {Eterea Paste on Elements}')
lx.eval('dialog.msg {Be sure to select some Vertices, Edges, Polygons or Items to paste your copied geo on them}')
lx.eval('dialog.open')
returnTo_originalStates()
sys.exit()
top_N = 100
# Dictionary to define plural words for each Selection Mode (to use in dialogs)
plural = {
'vertex': 'vertices',
'edge': 'edges',
'polygon': 'polygons',
'item': 'items'
}
def slownessDialog():
try:
# Set up the dialog
lx.eval('dialog.setup yesNo')
lx.eval('dialog.title {Eterea Place on Each Element: CAUTION}')
lx.eval('dialog.msg {You selected more than %s %s. This could be slow. Continue?}' % (top_N, plural[mySelMode]))
lx.eval('dialog.result ok')
# Open the dialog and see which button was pressed
lx.eval('dialog.open')
result = lx.eval('dialog.result ?')
except:
returnTo_originalStates()
sys.exit()
if mySelMode not in elements:
noElementsSelected_dialog()
elif mySelMode == 'vertex' and vertSelected_N == 0:
noElementsSelected_dialog()
elif mySelMode == 'edge' and edgeSelected_N == 0:
noElementsSelected_dialog()
elif mySelMode == 'polygon' and polySelected_N == 0:
noElementsSelected_dialog()
elif mySelMode == 'item' and itemSelected_N == 0:
noElementsSelected_dialog()
elif place_mode == 'each':
if mySelMode == 'vertex' and vertSelected_N > top_N:
slownessDialog()
elif mySelMode == 'edge' and edgeSelected_N > top_N:
slownessDialog()
elif mySelMode == 'polygon' and polySelected_N > top_N:
slownessDialog()
elif mySelMode == 'item' and itemSelected_N > top_N:
slownessDialog()
# -------------------------------------------------------------------------------
# Calculate Y dimensions in our copied prototype, for 'BASE' and 'TOP' alignment
# -------------------------------------------------------------------------------
# For 'CENTRAL' alignment we don't need any calculation
if align_mode == 'center':
myFinalCenY = '0.0'
# Calculate Y dimensions ONLY for 'Base' and 'Top'
else:
# Create a new temporal mesh item named 'protoBoundMesh' to paste our prototype
lx.eval('select.item scene001 set') # To ensure that our new item won't be child of any else
lx.eval('layer.new')
protoBoundMesh = lx.eval('query sceneservice selection ? mesh')
lx.eval('item.name {protoBoundMesh} mesh')
try:
lx.eval('!paste')
except:
lx.eval('dialog.setup info')
lx.eval('dialog.title {Eterea Paste on Elements}')
lx.eval('dialog.msg {Your clipboard is empty. Be sure to copy some geometry before using this tool}')
lx.eval('dialog.open')
returnTo_originalStates()
sys.exit()
# Calculate dimensions for Bounding Box
protoBound = lx.eval('query layerservice layer.bounds ? current')
# For 'Base' alignment (arg '1') /// NOTE: '[1]' is for -Y value and '[4]' for +Y
if align_mode == 'base':
myFinalCenY = (protoBound[4] - protoBound[1]) / 2
# For 'Top' alignment (arg '2')
else:
myFinalCenY = -((protoBound[4] - protoBound[1]) / 2)
# Delete temporal item mesh
lx.eval('item.delete')
# Return back to our original mesh
if mySelMode in components:
lx.eval('select.item %s set' % myCurrentMesh)
lx.eval('select.typeFrom %s' % mySelMode)
# -------------------------------------------------------------------------------
# Functions to paste using Paste Tool + Workplane Reset + Deselect stuff
# -------------------------------------------------------------------------------
def finalPaste_WorkplaneReset():
lx.eval('tool.set paste.tool on')
lx.eval('tool.attr paste.tool cenX 0.0')
lx.eval('tool.attr paste.tool cenY %s' % myFinalCenY)
lx.eval('tool.attr paste.tool cenZ 0.0')
lx.eval('tool.attr paste.tool sizeX 1.0')
lx.eval('tool.attr paste.tool sizeY 1.0')
lx.eval('tool.attr paste.tool sizeZ 1.0')
lx.eval('tool.attr paste.tool biasX 0.0')
lx.eval('tool.attr paste.tool biasY 0.0')
lx.eval('tool.attr paste.tool biasZ 0.0')
lx.eval('tool.apply')
lx.eval('tool.set paste.tool off 0')
lx.eval('workPlane.reset')
lx.eval('select.drop %s' % mySelMode)
# -------------------------------------------------------------------------------
# Function to avoid floating point errors comparing positions for 2 vertices
# NOTE: Thanks to James O'Hare aka Farfarer for this tip!
# -------------------------------------------------------------------------------
def nearlyEqual(posA, posB):
tolerance = 0.0000001
if abs(posA[0] - posB[0]) < tolerance:
if abs(posA[1] - posB[1]) < tolerance:
if abs(posA[2] - posB[2]) < tolerance:
return True;
return False;
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
#
#
# EACH VERTEX PROCEDURE
#
#
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if mySelMode == 'vertex' and place_mode == 'each':
# -------------------------------------------------------------------------------
# Function to paste on stray vertices
# -------------------------------------------------------------------------------
def pasteUsingHorizontalWorkplane():
lx.eval('select.element %s vertex set %s' % (myCurrentLayerID, eachVert_ID))
lx.eval('workPlane.fitSelect')
lx.eval('workPlane.rotate 2 90.0')
lx.eval('workPlane.rotate 1 90.0')
finalPaste_WorkplaneReset()
# -------------------------------------------------------------------------------
# Function to paste on vertices belonging to poly surfaces or splines
# -------------------------------------------------------------------------------
def pasteUsingVertexNormal():
# Query Normal Vector for Each Vert
lx.eval('query layerservice layer.index ? main')
eachVert_Normal = lx.eval('query layerservice vert.normal ? %s' % eachVert_ID)
# Select Each Vert (A) and align Workplane to it (default alignment here)
lx.eval('select.element %s vertex set %s' % (myCurrentLayerID, eachVert_ID))
lx.eval('workPlane.fitSelect')
# Create a new Temporal (B) point at same position than A-Normal coords,
# using our new local coordinates thanks to workplane
lx.eval('tool.set prim.makeVertex on 0')
lx.eval('tool.attr prim.makeVertex cenX %s' %eachVert_Normal[0])
lx.eval('tool.attr prim.makeVertex cenY %s' %eachVert_Normal[1])
lx.eval('tool.attr prim.makeVertex cenZ %s' %eachVert_Normal[2])
lx.eval('tool.apply')
# Align Workplane to these 2 points and rotate -90 in X and -90 in Y
lx.eval('workPlane.fitSelect')
lx.eval('workPlane.rotate 0 -90.0')
lx.eval('workPlane.rotate 1 -90.0')
# Deselect our original Each Vert (A) to securely delete the temporal one (B). Then Reselect A
lx.eval('select.element %s vertex remove %s' % (myCurrentLayerID, eachVert_ID))
lx.eval('delete') # This deletes Vertex B, only
lx.eval('select.element %s vertex set %s' % (myCurrentLayerID, eachVert_ID))
# Paste and drop vertex
finalPaste_WorkplaneReset()
lx.eval('select.drop vertex')
# -------------------------------------------------------------------------------
# For loop start
# -------------------------------------------------------------------------------
for eachVert_ID in vertSelected_ID:
lx.eval('query layerservice layer.index ? main')
# Query the number of polys connected to our vertex
vertPolysN = lx.eval('query layerservice vert.numPolys ? %s' % eachVert_ID)
# If no polys, these are stray vertices (the normal ones)
if vertPolysN == 0:
pasteUsingHorizontalWorkplane()
# We search now for that stray vertices that belongs to 1 poly, but this poly is a '1-point polygon'
elif vertPolysN == 1:
vertPolyList = lx.eval('query layerservice vert.polyList ? %s' % eachVert_ID)
polyNumVerts = lx.eval('query layerservice poly.numVerts ? %s' % vertPolyList)
# These are special stray vertices ('1-point polygons')
if polyNumVerts == 1:
pasteUsingHorizontalWorkplane()
# And for vertices belonging to 1 poly that are NOT 1-point polygons
else:
pasteUsingVertexNormal()
# For all other vertices (belonging to poly surfaces or splines)
else:
pasteUsingVertexNormal()
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
#
#
# EACH EDGE PROCEDURE
#
#
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
elif mySelMode == 'edge' and place_mode == 'each':
lx.eval('select.drop edge')
for eachEdge_ID in edgeSelected_ID:
# Iterate through edges to fit our workplane (this is a bit trickier than vertex or polys...)
indices = eachEdge_ID[1:-1]
indices = indices.split(',')
lx.eval('select.element %s edge set index:%s index2:%s' % (myCurrentLayerID, indices[0], indices[1]))
lx.eval('workPlane.fitSelect')
# Paste and drop vertex
finalPaste_WorkplaneReset()
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
#
#
# EACH POLYGON PROCEDURE
#
#
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
elif mySelMode == 'polygon' and place_mode == 'each':
# Create a new temporal mesh item named 'protoBackupLayer' where we will store
# a copy of our prototype (since we need to use our clipboard for other purposes)
lx.eval('layer.new')
protoBackupLayer = lx.eval('query sceneservice selection ? mesh')
lx.eval('item.name {protoBackupLayer} mesh')
lx.eval('paste')
# Return back to our original mesh
lx.eval('select.item %s set' % myCurrentMesh)
lx.eval('select.typeFrom polygon')
for eachPoly_ID in polySelected_ID:
# Check the type of poly (to avoid Bezier and Curves) and number of vertices (looking for quads)
lx.eval('query layerservice layer.index ? main')
eachPoly_type = lx.eval('query layerservice poly.type ? %s' % eachPoly_ID)
eachPoly_VertsN = lx.eval('query layerservice poly.numVerts ? %s' % eachPoly_ID)
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
# EACH QUAD PROCEDURE (to get a better Workplane alignment than Modo's default)
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# If we have a 4 points poly (quad), not a Curve and not a Bezier
if eachPoly_type != 'curve' and eachPoly_type != 'bezier' and eachPoly_VertsN == 4:
# Copy our selected quad
lx.eval('select.element %s polygon set %s' % (myCurrentLayerID, eachPoly_ID))
lx.eval('copy')
# Create a temporal mesh item called 'tempMeshHelper'
lx.eval('layer.new')
tempMeshHelper = lx.eval('query sceneservice selection ? mesh')
lx.eval('item.name {tempMeshHelper} mesh')
# Query layer ID, Paste polygon and Query Normal Vector for our selected polygon
tempMeshHelperID = lx.eval('query layerservice layer.index ? main')
lx.eval('paste')
selPolyNormal = lx.eval('query layerservice poly.normal ? 0') # '0' index because no other poly is on this ItemMesh
# ------------------------------------------------------------------------------------
# Function to determine the Sign ('+', '-' or '0') for each component 'XYZ' on Normal.
# This is important to check later if our workplane should be flipped on Y, or not,
# We use a tolerance to avoid problems with +0.0, -0.0 and 'machine epsilon' errors.
# ------------------------------------------------------------------------------------
def normalCompSign(compN):
tolerance = 0.0001 # Default: 0.0001 (change this in case of problems in some very specific cases)
if compN > tolerance:
return 1 # Means positive
elif compN < -tolerance:
return -1 # Means negative
else:
return 0 # Means zero, of course
compNX_sel = normalCompSign(selPolyNormal[0]) # Sign of Normal-X for our Selected poly
compNY_sel = normalCompSign(selPolyNormal[1]) # Sign of Normal-Y for our Selected poly
compNZ_sel = normalCompSign(selPolyNormal[2]) # Sign of Normal-Z for our Selected poly
# Subdivide flat. Select vertices '8' (for center origin) '5' (for X direction) and '3' (for XZ plane). Fit WP there
lx.eval('poly.subdivide flat')
lx.eval('select.typeFrom vertex')
lx.eval('select.element %s vertex set 8' % tempMeshHelperID)
lx.eval('select.element %s vertex add 5' % tempMeshHelperID)
lx.eval('select.element %s vertex add 3' % tempMeshHelperID)
lx.eval('workPlane.fitSelect')
lx.eval('workPlane.rotate 1 180.0')
# ---------------------------------------------------------------------------------------
# Delete all geo and create a single flat square 'Test-Normal-Poly' using this workplane
# ---------------------------------------------------------------------------------------
lx.eval('select.all')
lx.eval('delete')
lx.eval('tool.set prim.cube on 0')
lx.eval('tool.reset prim.cube')
axis = lx.eval('pref.value units.upAxis ?')
if axis == 0:
lx.eval('tool.attr prim.cube sizeX [0 m]')
lx.eval('tool.attr prim.cube sizeY [1 m]')
lx.eval('tool.attr prim.cube sizeZ [1 m]')
if axis == 1:
lx.eval('tool.attr prim.cube sizeX [1 m]')
lx.eval('tool.attr prim.cube sizeY [0 m]')
lx.eval('tool.attr prim.cube sizeZ [1 m]')
if axis == 2:
lx.eval('tool.attr prim.cube sizeX [1 m]')
lx.eval('tool.attr prim.cube sizeY [1 m]')
lx.eval('tool.attr prim.cube sizeZ [0 m]')
lx.eval('tool.apply')
lx.eval('tool.set prim.cube off 0')
# Check Normal for our Test Poly
lx.eval('select.typeFrom polygon')
lx.eval('select.all')
testPolyIndex = lx.eval('query layerservice polys ? selected')
testPolyNormal = lx.eval('query layerservice poly.normal ? %s' % testPolyIndex)
compNX_test = normalCompSign(testPolyNormal[0]) # Sign of Normal-X for our Test poly
compNY_test = normalCompSign(testPolyNormal[1]) # Sign of Normal-Y for our Test poly
compNZ_test = normalCompSign(testPolyNormal[2]) # Sign of Normal-Z for our Test poly
# In case that some component for this Normal Vector has a different sign than the
# original quad, proceed to 'flip' Y axis (rotating Z 180 degrees)
# NOTE: we only compare 'sign' changes and not values, since our original poly could be
# non-planar and in that case, even if the normal could be 'similar' the values would be
# completely different. Comparing a complete change of direction (sign) for
# normal vector components seems a better method to test this issue.
if compNX_sel != compNX_test or compNY_sel != compNY_test or compNZ_sel != compNZ_test:
lx.eval('workPlane.rotate 2 180.0')
# Delete actual item mesh ('tempMeshHelper') and go back to our 'protoBackupLayer' to re-copy our prototype
lx.eval('item.delete')
lx.eval('select.item {protoBackupLayer} add')
lx.eval('select.typeFrom polygon')
lx.eval('select.all')
lx.eval('copy')
# Return back to our original mesh, Paste and Drop vertex
lx.eval('select.item %s set' % myCurrentMesh)
finalPaste_WorkplaneReset()
lx.eval('select.drop polygon')
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
# EACH NON-QUAD PROCEDURE (For all other kind of polys)
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
else:
# Select our poly
lx.eval('select.element %s polygon set %s' % (myCurrentLayerID, eachPoly_ID))
lx.eval('workPlane.fitSelect')
finalPaste_WorkplaneReset()
# Go back to our 'protoBackupLayer', Delete this provisional item mesh and Return back to our original mesh
lx.eval('select.item {protoBackupLayer} set')
lx.eval('item.delete')
lx.eval('select.item %s set' % myCurrentMesh)
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
#
#
# ITEM PROCEDURES
#
#
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
elif mySelMode == 'item':
# -------------------------------------------------------------------------------------------------------------------
# We need to be sure if the Item Mesh in which we will paste our geometry is empty or not,
# because PasteTool needs to 'see' something in our mesh to work (even if that 'something' is not used at all...)
# We use this function to add a temporal Unit Cube, storing it on a Selection Set, to Delete at end
# -------------------------------------------------------------------------------------------------------------------
def tempGeoForPasteTool():
lx.eval('select.typeFrom polygon')
lx.eval('script.run "macro.scriptservice:32235710027:macro"') # Adds a default unit cube
lx.eval('select.all')
lx.eval('select.editSet tempCubeSet add')
lx.eval('select.typeFrom item')
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
# ONLY ONE ITEM MESH SELECTED PROCEDURE (both for 'EACH' and 'GROUP')
# [ We want to paste our geometry inside that Item Mesh ]
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if itemSelected_N == 1 and itemSelected_Type == 'mesh':
# Query Pos, Rot and Scale for our Selected Item Mesh (since Paste Tool doesn't works fine with PRS-modified Items)
myCM_P = lx.eval('query sceneservice item.pos ? %s' % itemSelected_ID)
myCM_R = lx.eval('query sceneservice item.rot ? %s' % itemSelected_ID)
myCM_S = lx.eval('query sceneservice item.scale ? %s' % itemSelected_ID)
# If our ItemsMesh has been PRS-modified in some way: Reset All (we will recover these values later)
if any([myCM_P[0], myCM_P[1], myCM_P[2], myCM_R[0], myCM_R[1], myCM_R[2]]) or myCM_S[0] != 1 or myCM_S[1] != 1 or myCM_S[2] != 1:
lx.eval('select.item %s' % itemSelected_ID)
lx.eval('transform.reset all')
myCM_PRS = True
# Do nothing if all is PRS-default
else:
myCM_PRS = False
# We want to know if our selected Item Mesh is empty or not
lx.eval('query layerservice layer.index ? selected')
selectedMesh_N = lx.eval('query layerservice vert.N ? all')
# If empty, add a simple cube using our tempGeoForPasteTool function
if selectedMesh_N == 0:
tempGeoForPasteTool()
# Paste our geometry
lx.eval('select.typeFrom polygon')
finalPaste_WorkplaneReset()
# Delete our helper tempCubeSet if our selected Item Mesh was originally empty
if selectedMesh_N == 0:
lx.eval('select.drop polygon')
lx.eval('select.useSet tempCubeSet select')
lx.eval('delete')
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
# VARIOUS ITEMS PROCEDURE
# [ We need to paste our geometry on a 'helper' separate Item Mesh ]
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
else:
# Lets check if our new helper '---PASTED---' mesh (to paste things) exists or not
try:
lx.eval('query sceneservice locator.name ? ---PASTED---')
pasted_Existance = True
except:
pasted_Existance = False
# Create the Item Mesh if it doesn't exists and add our Unit Cube (just to allow working the Paste Tool)
if not pasted_Existance:
lx.eval('layer.new')
lx.eval('query sceneservice selection ? mesh')
lx.eval('item.name {---PASTED---} mesh')
tempGeoForPasteTool()
# If our helper mesh exists BUT it's empty, add a temporal Unit Cube
if pasted_Existance:
lx.eval('select.item ---PASTED---')
lx.eval('query layerservice layer.index ? selected')
pasted_VertsN = lx.eval('query layerservice vert.N ? all')
if pasted_VertsN == 0:
tempGeoForPasteTool()
# ----------------------------------------------------------------------
# Function to Align Workplane to Items and Paste geometry on helper mesh
# ----------------------------------------------------------------------
def finalPaste_onItems():
lx.eval('workPlane.fitSelect')
lx.eval('select.item {---PASTED---} set')
lx.eval('select.typeFrom polygon')
finalPaste_WorkplaneReset()
# ----------------------------------------------------------------------------------------------
# Function to return back to our helper mesh to drop all and remove the Unit Cube (if necessary)
# ----------------------------------------------------------------------------------------------
def goBack_toPastedItem():
lx.eval('select.item {---PASTED---} set')
lx.eval('select.drop polygon')
if pasted_Existance == False or pasted_VertsN == 0:
lx.eval('select.useSet tempCubeSet select')
lx.eval('delete')
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
# EACH ITEM PROCEDURE FOR VARIOUS ITEMS
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if place_mode == 'each':
# Loop for every selected Item to fit our workplane and return back to our helper mesh to paste geometry
for eachItem_ID in itemSelected_ID:
lx.eval('select.item {%s} set' % eachItem_ID)
finalPaste_onItems()
goBack_toPastedItem()
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
# GROUP ITEM PROCEDURE FOR VARIOUS ITEMS
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
elif place_mode == 'group':
# Align workplane to selected Items to use as reference for our final paste functions
lx.eval('select.drop item')
lx.eval('select.useSet tempSetA select')
finalPaste_onItems()
goBack_toPastedItem()
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#
#
#
# GROUP PROCEDURE FOR ALL KIND OF COMPONENTS (Vertex, Edge and Polygon)
#
#
#
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
elif mySelMode in components and place_mode == 'group':
lx.eval('select.useSet tempSetA select')
lx.eval('workPlane.fitSelect')
finalPaste_WorkplaneReset()
lx.eval('select.drop %s' % mySelMode)
# Return back to our original States
returnTo_originalStates()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment