Skip to content

Instantly share code, notes, and snippets.

@Eterea
Last active August 29, 2015 13:56
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/9273155 to your computer and use it in GitHub Desktop.
Save Eterea/9273155 to your computer and use it in GitHub Desktop.
Modo Script to paste and orient geometry on a bunch of selected end vertices of Lines, Curves or Beziers
#python
# ----------------------------------------------------------------------------------------------
# NAME: etr_pasteOnSplinesEnd.py
# VERS: 1.0
# DATE: July 19, 2014
#
# MADE: Cristobal Vila, etereaestudios.com
# Thanks to James O'Hare aka Farfarer, Arnie Cachelin and er_9 for your help! :-)
#
# USES: To paste and orient geometry on a bunch of selected vertices of a LINE, CURVE or BEZIER.
#
# Use '@etr_pasteOnLineEnd.py center' for Bounding Box CENTER alignment
# Use '@etr_pasteOnLineEnd.py base' for Bounding Box BASE alignment
# Use '@etr_pasteOnLineEnd.py top' for Bounding Box TOP alignment
#
# Copy your “prototype” mesh to align it at end of selected splines: lines, curves or bezier.
# You can place it on several 'spline-ends' at a time, even mixing type of splines.
# You can select some specific vertices (using vertex selection mode),
# or you can select a bunch of geometry using a lasso (on vertex, edge or poly mode).
# No matter the method you use, the script will remove any 'non-ending' point in your selection.
# The script will take care of your previous workplane, symmetry state and selection mode.
#
# HOW THIS SCRIPT WORKS, INTERNALLY? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
#
# LINES AND POLYLINES
# Once we have an end vertex, select the neighbor and align the Workplane there, for placement.
# In order to avoid problems with flipped workplanes, we create a new pair of vertex there,
# to ensure that the one with lower index one will be the center, and other will define direction.
#
# BEZIERS
# We use the end vertice for workplane position and bezier-point for alignement.
# In case that bezier dimension is 0, we use the neighbor's bezier.
#
# CURVES
# Since we have no way to access to end-direction-vector, we use a small trick here:
# freeze a copy of original curve (or a small portion of it, for the more lenght ones)
# to use the small freezed segment at end for workplane position and alignment.
# ----------------------------------------------------------------------------------------------
# Defining argument
myarg = lx.arg()
# 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()
components = ['vertex', 'edge', 'polygon']
# Function with Dialog to abort if no-Components are selected
if mySelMode not in components:
lx.eval('dialog.setup info')
lx.eval('dialog.title {Eterea Paste on Splines End}')
lx.eval('dialog.msg {You need to be in Component Mode to use this script (Vertex, Edge or Poly)\n\
Select Lines, Curves or Beziers and be sure to Select your Current Mesh on Item List}')
lx.eval('dialog.open')
sys.exit()
# 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')
# Query Current Active Mesh and Layer ID
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
if myCurrentMesh == None:
lx.eval('dialog.setup info')
lx.eval('dialog.title {Eterea Paste on Splines End}')
lx.eval('dialog.msg {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
copyDeSelectionState = lx.eval('pref.value application.copyDeSelection ?')
pasteSelectionState = lx.eval('pref.value application.pasteSelection ?')
symmetryState = lx.eval('select.symmetryState ?')
# 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)
# -------------------------------------------------------------------------------------
# Recover our original ItemMesh PRS values (if modified)
# -------------------------------------------------------------------------------------
if myCM_PRS == True:
lx.eval("select.item %s" % myCurrentMesh)
returnItemTo_originalPRS()
# -------------------------------------------------------------------------------
# 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')
# -------------------------------------------------------------------------------
# Security checks and measures for different types of selections
# -------------------------------------------------------------------------------
# Query the number of Edges and Polygons selected (not neccesary with vertices, for now)
polySelected_N = lx.eval('query layerservice poly.N ? selected')
edgeSelected_N = lx.eval('query layerservice edge.N ? selected')
# Function to leave 1-edge-vertex selections only ('end vertices' for Lines, Curves or Bezier)
def leave_1edgeVertex_Only():
lx.eval('select.vertex remove edge more 4')
lx.eval('select.vertex remove edge equal 4')
lx.eval('select.vertex remove edge equal 3')
lx.eval('select.vertex remove edge equal 2')
lx.eval('select.vertex remove edge equal 0')
# Function with Dialog to abort if no-verts at all are selected
def noVertSelected_dialog():
lx.eval('dialog.setup info')
lx.eval('dialog.title {Eterea Paste on Splines End}')
lx.eval('dialog.msg {Be sure to select some END Vertices, Edges or Polygons\nthat are part of Lines, Curves or Beziers}')
lx.eval('dialog.open')
returnTo_originalStates()
sys.exit()
# If we have VERTICES selected, discard the 'not-at-end' ones, only.
# This could leave us with NO verts selected, at all. We have a check and dialog, later, to solve this.
if mySelMode == 'vertex':
leave_1edgeVertex_Only()
# If we have EDGES selected, select all connected, convert to vertices and discard the 'not-at-end' ones
elif mySelMode == 'edge' and edgeSelected_N != 0:
lx.eval('select.connect')
lx.eval('select.convert vertex')
leave_1edgeVertex_Only()
# If we have POLYS selected, select all connected, convert to vertices and discard the 'not-at-end' ones
elif mySelMode == 'polygon' and polySelected_N != 0:
lx.eval('select.connect')
lx.eval('select.convert vertex')
leave_1edgeVertex_Only()
# For all other situations abort securely
else:
noVertSelected_dialog()
# ---------------------------------------------------------------------------------------
# If we arrive here, we have 'end vertices' selected, ONLY (or maybe NONE at all...)
# ---------------------------------------------------------------------------------------
# Query the Index and Number of selected vertices
vertSelected = lx.evalN('query layerservice verts ? selected')
vertSelected_N = lx.eval('query layerservice vert.N ? selected')
lx.out('End Vertices Processed:', vertSelected_N)
# ---------------------------------------------------------------------------------------
# Dialogs notifying about 'none' -OR- slowness using a high number of vertices (For +100)
# ---------------------------------------------------------------------------------------
if vertSelected_N == 0:
noVertSelected_dialog()
elif vertSelected_N > 100:
try:
# Set up the dialog
lx.eval('dialog.setup yesNo')
lx.eval('dialog.title {Eterea Place on Splines End: CAUTION}')
lx.eval('dialog.msg {You have more than 100 end vertices selected.\n\
This could be slow, specially using dense meshes. Continue?}')
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()
# -------------------------------------------------------------------------------
# Deselect all vertices
# -------------------------------------------------------------------------------
lx.eval('select.drop vertex')
# -------------------------------------------------------------------------------
# Calculate Y dimensions in our copied prototype, for 'BASE' and 'TOP' alignment
# -------------------------------------------------------------------------------
# For 'CENTRAL' alignment we don't need any calculation
if myarg == '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('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 Place on Splines End}')
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 myarg == '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
lx.eval('select.item %s add' % myCurrentMesh)
# -------------------------------------------------------------------------------
# Function to paste using Paste Tool + Workplane Reset + Deselect
# -------------------------------------------------------------------------------
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 vertex')
# -------------------------------------------------------------------------------
# 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;
# ///////////////////////////////////////////////////////////////////////////////////////
#
#
#
#
# FOR-LOOP START ... TO PASTE GEOMETRY ON EACH SEPARATE VERTEX
#
#
#
#
# ///////////////////////////////////////////////////////////////////////////////////////
for myEachVert in vertSelected:
# -------------------------------------------------------------------------------
# Queries for each vertex 'A' and also the neighbor one 'B'
# -------------------------------------------------------------------------------
# Query our Current Active Layer ID
myCurrentLayerID = lx.eval('query layerservice layer.index ? main')
# Save Position for Vertex 'A'
vertA_position = lx.eval('query layerservice vert.wpos ? %s' % myEachVert)
# Query Polygon where Vertex 'A' belongs
polyA = lx.eval('query layerservice vert.polyList ? %s' % myEachVert)
# Query Type of Polygon
polyA_Type = lx.eval('query layerservice poly.type ? %s' % polyA)
# Query number of vertices on that Polygon
vertexN_inPolyA = lx.eval('query layerservice poly.numVerts ? %s' % polyA)
# /////////////////////////////////////////////////////////////////////////////////////
#
# BEZIER PROCEDURE
#
# /////////////////////////////////////////////////////////////////////////////////////
if polyA_Type == 'bezier':
# Query all vertices belonging to that bezier
polyA_vertList = lx.eval('query layerservice poly.vertList ? %s' % polyA)
# ----- REMINDER -------------------------------------
# polyA_vertList[0] # First point Index
# polyA_vertList[1] # First point-bezier-out Index
# polyA_vertList[2] # Second point Index
# polyA_vertList[-1] # Last point Index
# polyA_vertList[-2] # Last point-bezier-in Index
# polyA_vertList[-3] # Penultimate point Index
# ----------------------------------------------------
# Query Position for First, Last and Beziers of these points
polyA_firstVert_pos = lx.eval('query layerservice vert.wpos ? %s' % polyA_vertList[0]) # First point Position
polyA_firstBezi_pos = lx.eval('query layerservice vert.wpos ? %s' % polyA_vertList[1]) # First point-bezier-out Position
polyA_lastVert_pos = lx.eval('query layerservice vert.wpos ? %s' % polyA_vertList[-1]) # Last point Position
polyA_lastBezi_pos = lx.eval('query layerservice vert.wpos ? %s' % polyA_vertList[-2]) # Last point-bezier-in Position
# NOTE: not sure why, but I need to use strings 'str()' to make working next lines.
# In other case I receive 'false' lx.out returns for equal indexes (?)
# If our vertex A is the FIRST vertex in bezier and tangent has a length different from 0
if str(myEachVert) == str(polyA_vertList[0]) and polyA_firstVert_pos != polyA_firstBezi_pos:
# Then our vertex B will be defined by first-point-tangent position
vertB_index = polyA_vertList[1]
# If our vertex A is the FIRST vertex in bezier and tangent has a length equal to 0 (choose second point)
elif str(myEachVert) == str(polyA_vertList[0]) and polyA_firstVert_pos == polyA_firstBezi_pos:
# Then our vertex B will be defined by second point position
vertB_index = polyA_vertList[2]
# If our vertex A is the LAST vertex in bezier and tangent has a length different from 0
elif str(myEachVert) == str(polyA_vertList[-1]) and polyA_lastVert_pos != polyA_lastBezi_pos:
# Then our vertex B will be defined by last-point-tangent position
vertB_index = polyA_vertList[-2]
# If our vertex A is the LAST vertex in bezier and tangent has a length equal to 0 (choose second point)
elif str(myEachVert) == str(polyA_vertList[-1]) and polyA_lastVert_pos == polyA_lastBezi_pos:
# Then our vertex B will be defined by penultimate point position
vertB_index = polyA_vertList[-3]
# Select that vertices 'A' and 'B'
lx.eval('select.element %s vertex set %s' % (myCurrentLayerID, myEachVert))
lx.eval('select.element %s vertex add %s' % (myCurrentLayerID, vertB_index))
# -------------------------------------------------------------------------------
# Now we can securely align our Workplane to these vertices
# -------------------------------------------------------------------------------
# Align Workplane and rotate +90 in X
lx.eval('workPlane.fitSelect')
lx.eval('workPlane.rotate 0 90.0')
# Function to paste using Paste Tool + Workplane Reset + Deselect
finalPaste_WorkplaneReset()
# /////////////////////////////////////////////////////////////////////////////////////
#
# CURVE PROCEDURE (excluding curves with only 2 points)
#
# /////////////////////////////////////////////////////////////////////////////////////
elif polyA_Type == 'curve' and vertexN_inPolyA > 2:
# 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 add' % myCurrentMesh)
# Select our vertex 'A' only
lx.eval('select.element %s vertex set %s' % (myCurrentLayerID, myEachVert))
# Save our vertex selection in a Selection Set
lx.eval('select.editSet tempSetA add')
# Select and copy our Curve (polyA)
lx.eval('select.typeFrom polygon')
lx.eval('select.element %s polygon set %s' % (myCurrentLayerID, polyA))
lx.eval('copy')
# Create another temporal mesh item named 'tempMeshHelper'
lx.eval('layer.new')
tempMeshHelper = lx.eval('query sceneservice selection ? mesh')
lx.eval('item.name {tempMeshHelper} mesh')
# Paste our Curve
lx.eval('paste')
# If we have a curve with more that 3 points this is to use the neccesary part only,
# to agilize all with long curves, storing only the 2 first segments, and deleting the rest.
# The first segment for our curve will remaing accurate, once we freeze this, later
if vertexN_inPolyA > 3:
# Select our Vertex A using our tempSetA Set:
lx.eval('select.typeFrom vertex')
lx.eval('select.useSet tempSetA select')
# Grow Selection twice, convert to edge, invert selection, delete and convert to poly
lx.eval('select.expand')
lx.eval('select.expand')
lx.eval('select.convert edge')
lx.eval('select.invert')
lx.eval('delete')
lx.eval('select.typeFrom polygon')
# Freeze our curve and select all vertices
lx.eval('poly.freeze false false 2 true true true')
lx.eval('select.typeFrom vertex')
lx.eval('select.all')
# Query the Index of vertices
tempMeshHelperID = lx.eval('query layerservice layer.index ? main')
freezedVertID = lx.evalN('query layerservice verts ? selected')
# Query Position for First, Second, Last and Penultimate of these points
firstFreezedVert_pos = lx.eval('query layerservice vert.wpos ? %s' % freezedVertID[0]) # First point Position
seconFreezedVert_pos = lx.eval('query layerservice vert.wpos ? %s' % freezedVertID[1]) # Second point Position
lastFreezedVert_pos = lx.eval('query layerservice vert.wpos ? %s' % freezedVertID[-1]) # Last point Position
penuFreezedVert_pos = lx.eval('query layerservice vert.wpos ? %s' % freezedVertID[-2]) # Penultimate point Position
# Deselect all vertices
lx.eval('select.drop vertex')
# If our originally selected point ('vert_A') has the same Position than the FIRST point on this freezed curve,
# then use the TWO first vertices positions to create NEW vertices to fit our Workplane.
#
# NOTES:
# We need to create NEW vertices, and not using the existing ones, since by this way we guarantee that
# 'Start' vertex has a LOWER INDEX than 'End' vertex, something that was not guaranteed using existing geo,
# and something crucial to get a predictable orientation in our WP.
#
# We use the 'nearlyEqual' funtion to avoid floating point error calculations comparing positions for 2 vertex
if nearlyEqual(vertA_position, firstFreezedVert_pos):
# Create both Vertices
lx.eval('tool.set prim.makeVertex on 0')
lx.eval('tool.attr prim.makeVertex cenX %s' %firstFreezedVert_pos[0])
lx.eval('tool.attr prim.makeVertex cenY %s' %firstFreezedVert_pos[1])
lx.eval('tool.attr prim.makeVertex cenZ %s' %firstFreezedVert_pos[2])
lx.eval('tool.apply')
lx.eval('tool.attr prim.makeVertex cenX %s' %seconFreezedVert_pos[0])
lx.eval('tool.attr prim.makeVertex cenY %s' %seconFreezedVert_pos[1])
lx.eval('tool.attr prim.makeVertex cenZ %s' %seconFreezedVert_pos[2])
lx.eval('tool.apply')
lx.eval('tool.set prim.makeVertex off 0')
# If our originally selected point ('vert_A') has the same Position than the LAST point on this freezed curve,
# then use the TWO last vertices positions to create NEW vertices to fit our Workplane.
elif nearlyEqual(vertA_position, lastFreezedVert_pos):
# Create both Vertices
lx.eval('tool.set prim.makeVertex on 0')
lx.eval('tool.attr prim.makeVertex cenX %s' %lastFreezedVert_pos[0])
lx.eval('tool.attr prim.makeVertex cenY %s' %lastFreezedVert_pos[1])
lx.eval('tool.attr prim.makeVertex cenZ %s' %lastFreezedVert_pos[2])
lx.eval('tool.apply')
lx.eval('tool.attr prim.makeVertex cenX %s' %penuFreezedVert_pos[0])
lx.eval('tool.attr prim.makeVertex cenY %s' %penuFreezedVert_pos[1])
lx.eval('tool.attr prim.makeVertex cenZ %s' %penuFreezedVert_pos[2])
lx.eval('tool.apply')
lx.eval('tool.set prim.makeVertex off 0')
# Align Workplane and rotate +90 in X
lx.eval('workPlane.fitSelect')
lx.eval('workPlane.rotate 0 90.0')
# Delete actual item mesh ('tempMeshHelper')
lx.eval('item.delete')
# Return back to our 'protoBackupLayer' to re-copy our prototype
lx.eval('select.item {protoBackupLayer} add')
lx.eval('select.typeFrom polygon')
lx.eval('select.all')
lx.eval('copy')
# Delete actual item mesh ('protoBackupLayer')
lx.eval('item.delete')
# Return back to our original mesh
lx.eval('select.item %s add' % myCurrentMesh)
# Remove selection set
lx.eval('select.typeFrom vertex')
lx.eval('!!select.deleteSet tempSetA')
# Function to paste using Paste Tool + Workplane Reset + Deselect
finalPaste_WorkplaneReset()
# /////////////////////////////////////////////////////////////////////////////////////
#
# LINE PROCEDURE (including curves with only 2 points)
#
# /////////////////////////////////////////////////////////////////////////////////////
else:
# Since we deselected all, now re-select our vertex 'A' only
lx.eval('select.element %s vertex set %s' % (myCurrentLayerID, myEachVert))
# Save our vertex selection in a Selection Set
lx.eval('select.editSet tempSetA add')
# Grow Selection (this will add a second vertex 'B' to selection)
lx.eval('select.expand')
# Subtract tempSetA to selection (now we have only that vertex B selected)
lx.eval('select.useSet tempSetA deselect')
# Remove selection set
lx.eval('!!select.deleteSet tempSetA')
# Query and Save Position for Selected Vertex 'B'
vertB_index = lx.eval('query layerservice verts ? selected')
vertB_position = lx.eval('query layerservice vert.wpos ? %s' % vertB_index)
# Deselect all vertices
lx.eval('select.drop vertex')
# -------------------------------------------------------------------------------
# Add a new Start Vertex with same position than vert 'A'
# Add a new End Vertex with same position than vert 'B'
# This is an important step, since by this way we guarantee that
# 'Start' vertex has a LOWER INDEX than 'End' vertex,
# something that was not guaranteed using initial geometry,
# and something crucial to get a predictable orientation in our WP
# -------------------------------------------------------------------------------
# Create both new points
lx.eval('tool.set prim.makeVertex on 0')
lx.eval('tool.attr prim.makeVertex cenX %s' %vertA_position[0])
lx.eval('tool.attr prim.makeVertex cenY %s' %vertA_position[1])
lx.eval('tool.attr prim.makeVertex cenZ %s' %vertA_position[2])
lx.eval('tool.apply')
lx.eval('tool.attr prim.makeVertex cenX %s' %vertB_position[0])
lx.eval('tool.attr prim.makeVertex cenY %s' %vertB_position[1])
lx.eval('tool.attr prim.makeVertex cenZ %s' %vertB_position[2])
lx.eval('tool.apply')
lx.eval('tool.set prim.makeVertex off 0')
# -------------------------------------------------------------------------------
# Now we can securely align our Workplane to these temporal vertices
# -------------------------------------------------------------------------------
# Align Workplane to 'Start' (lower index) and 'End' (higher index)
lx.eval('workPlane.fitSelect')
# Rotate Workplane +90 in X
lx.eval('workPlane.rotate 0 90.0')
# Delete recently created points
lx.eval('delete')
# Function to paste using Paste Tool + Workplane Reset + Deselect
finalPaste_WorkplaneReset()
# ///////////////////////////////////////////////////////////////////////////////////////
#
#
#
#
# FOR-LOOP END ... GEOMETRY PASTED ON EACH SEPARATE VERTEX
#
#
#
#
# ///////////////////////////////////////////////////////////////////////////////////////
# 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