 # Closet Point/Edges to locator

#1

I have a locator close to a geo. How do I make the locator snap to the closet CV on the Geo?

#2

one way might be to use the polySelectConstraint command to filter those components within a certain distance, then loop through each remaining component, finding the closest.
I imagine it’d be fairly slow though.

#3

If you meant the closest vertex on a mesh…
Create a closestPointOnMesh node.
Connect your poly.outMesh to the closestPointOnMesh.inputMesh
Connect your locator.translate to the closestPointOnMesh.inPosition
Query the position of closestPointOnMesh.closestVertexIndex.
Move your locator to this point.

``````
string \$mesh[] = `polySphere -r 1 -sx 20 -sy 20 -ax 0 1 0 -cuv 2 -ch 1`;
string \$null[] = `spaceLocator -p 0 0 0`;
// Move the locator somewhere
move -r -0.433105 0.314959 0.946469 ;
string \$cpom = `createNode closestPointOnMesh`;
connectAttr -f (\$mesh + ".outMesh") (\$cpom + ".inMesh");
connectAttr -f (\$null + ".translate") (\$cpom + ".inPosition");
int \$closestVert = `getAttr (\$cpom + ".closestVertexIndex")`;
float \$vtxPos[] = `xform -q -ws -t (\$mesh + ".vtx[" + \$closestVert + "]")`;
xform -ws -t \$vtxPos \$vtxPos \$vtxPos \$null;

``````
`````` I'm sure there's a better way to do that.

If you want the closest CV on a NURBS surface, you could use the closestPointOnSurface node. This gives you a surface point, which I'm guessing is what you want to snap to. But if you do want to snap to the closest CV that is a little trickier.

Anybody?``````

#4

Are talking mel or python, and is that about NURBS or polys? Guessing from the “CV” int the question i think you are talking for NURBS. The closestPointOnSurface is a fine way for a mel approach, but as far as I know it requres the bonus tools pack, which meens an additional plug-in t be loaded. If you are familiar with python and need to snap exactly to a cv/vtx - few simple calls to the API can make it fast and easy.

#5

Sorry about the title. I need mean a vertex on a poly, but would curious to know how to do on a Nurbs also.

I write my scripts in Python, so I would love it if you get let me know how to do via a API.

For now, I would try 3d monkey method.
What is the bonus tool pack? Is that part of Maya or a additional plugin?

#6

Does it really needs to be vertices/cvs ? If so, then use python OpenMaya module. It would look somewhat like this:

``````
import maya.OpenMaya as om

targetFn = om.MFnMesh(targetDag)

locPos = om.MPoint(1.2,0.4,0.2)

#returned closest point
closestPoint = om.MPoint()

#returned int ptr to closest face
mutil = om.MScriptUtil()
intPtr = mutil.asIntPtr()

#find the closest point and id
targetFn.getClosestPoint(locPos,closestPoint,om.MSpace.kWorld,intPtr)

#get the closest face
closestPolygonId = om.MScriptUtil(intPtr).asInt()

#setIndex requires a ptr to previous index
dummyPtr = mutil.asIntPtr()

polyIter = om.MItMeshPolygon(targetDag)
polyIter.setIndex(closestPolygonId,dummyPtr)

#get vertices from this face
pArray = om.MPointArray()
vArray = om.MIntArray()
polyIter.getPoints(pArray,om.MSpace.kWorld)
polyIter.getVertices(vArray)

#===then just compare which vertex is the closest to the locator.

``````

otherwise, just use geometry constraint to get the closest point on mesh.

#7

Here it is if you want snapping just to vertices, not to closestPoint:

``````
import maya.OpenMaya as om
import maya.cmds as mc

def snapToClosest():
selObj = om.MSelectionList()
# Store active selection
om.MGlobal.getActiveSelectionList(selObj)
if selObj.length() < 2:
mc.warning("Select geo and then locator")
return
locDag = om.MDagPath()
meshDag = om.MDagPath()
# extract dag paths, assuming the first selected is the mesh
# and the second is the locator
selObj.getDagPath(0, meshDag)
selObj.getDagPath(1, locDag)
locName = locDag.fullPathName()
# get the locator position
locT = om.MFnTransform( locDag.transform() )
locPt = om.MPoint( locT.getTranslation(om.MSpace.kPostTransform) )

# find the closest vertex
vtxIt = om.MItMeshVertex(meshDag)
mindist = 10000
closestPt = om.MPoint()
vtxId = -1
while not vtxIt.isDone():
vtxPt = vtxIt.position( om.MSpace.kWorld)
dist = vtxPt.distanceTo(locPt)
if dist < mindist:
mindist = dist
closestPt = vtxPt
vtxId = vtxIt.index()
vtxIt.next()
print "Closest vtx: %d at %f" % (vtxId, mindist)
mc.xform(locName, t=[closestPt.x, closestPt.y, closestPt.z], ws=1, a=1)

snapToClosest()

``````

You will need to extract a dag path for your objects in order to attach an iterator to them. If you need to pass the objects by name, not as selection you can use the following routine:

``````
selObj = om.MSelectionList()
locDag = om.MDagPath()
meshDag = om.MDagPath()
# extract dag paths for the objects
selObj.getDagPath(0, meshDag)
selObj.getDagPath(1, locDag)
# check them if you like
meshName = meshDag.fullPathName()
locName = locDag.fullPathName()
# .. proceed as before

``````

It’s exactly the same for NURBS surfaces ot curves, just you have to use the proper iterator class also with MItMeshEdge for example you can snap to edge centers, start/end points and so on.

#8

Thanks r4inm4ker for your help. Really appreciate it

Azrail, the script works fantastically on meshes, however when I try to use it on Nurbs surface, the locator seems to snap to the CV on the seam/isoparm

Here is the modified code

``````
import maya.OpenMaya as om
import maya.cmds as mc

def snapToClosest():
selObj = om.MSelectionList()
# Store active selection
om.MGlobal.getActiveSelectionList(selObj)
if selObj.length() < 2:
mc.warning("Select geo and then locator")
return
locDag = om.MDagPath()
meshDag = om.MDagPath()
# extract dag paths, assuming the first selected is the mesh
# and the second is the locator
selObj.getDagPath(0, meshDag)
selObj.getDagPath(1, locDag)
locName = locDag.fullPathName()
# get the locator position
locT = om.MFnTransform( locDag.transform() )
locPt = om.MPoint( locT.getTranslation(om.MSpace.kPostTransform) )

# find the closest vertex
vtxIt = om.MItSurfaceCV(meshDag)
mindist = 10000000
closestPt = om.MPoint()
vtxId = -1
while not vtxIt.isDone():
vtxPt = vtxIt.position( om.MSpace.kWorld)
dist = vtxPt.distanceTo(locPt)
if dist < mindist:
mindist = dist
closestPt = vtxPt
vtxId = vtxIt.index()
vtxIt.next()
print "Closest vtx: %d at %f" % (vtxId, mindist)
mc.xform(locName, t=[closestPt.x, closestPt.y, closestPt.z], ws=1, a=1)

snapToClosest()

``````

This OpenMaya module is completely new to me. Are there any good tutorial/books apart from the documentation which elaborates a bit more.

#9

The openMaya module is the python implementation of maya’s API, so the docs in maya’s help describe all of it, but you have to translate it from C++ syntax. There are few tutorials online which can help you start understanding what exactly is going on:

http://www.rtrowbridge.com/blog/2009/02/maya-api-docs-demystified-for-python-users/

As for the snapping - nurbs requre a little more specific approach, since the iteration is processed in rows:

``````
while not vtxIt.isDone():
while not vtxIt.isRowDone():
vtxPt = vtxIt.position( om.MSpace.kWorld)
dist = vtxPt.distanceTo(locPt)
if dist < mindist:
mindist = dist
closestPt = vtxPt
vtxId = vtxIt.index()
vtxIt.next()
vtxIt.nextRow()

``````

btw it still snaps somewhere close to the surface…

#10