Skip to content

Instantly share code, notes, and snippets.

@thirstydevil
Last active March 17, 2017 17:09
Show Gist options
  • Save thirstydevil/817c335c356e8c231d35fc013f654bfd to your computer and use it in GitHub Desktop.
Save thirstydevil/817c335c356e8c231d35fc013f654bfd to your computer and use it in GitHub Desktop.
def locatorOnPoly(name=""):
"""
Requires a single poly selection. The script will then create a locator of the polygon normal and then orient it
to the tangent of the longest edge on the polygon using a aim constraint and and a couple of locators that finally
get cleaned up. Probably a nice way to do this by building a world space rotation matrix from the vector of
point A and B on the longest edge and the normal of the poly.
:param name: str(Locator Name)
:return: pCore.dt.transform()
"""
face = pCore.selected(fl=True)[0]
if len(face) == 1:
if isinstance(face, pCore.MeshFace):
with pCore.UndoChunk():
# Create the locator of the poly normal using the moveTool manipulator to do that
currentTool = pCore.currentCtx()
pCore.setToolTo("moveSuperContext")
pos = pCore.dt.Vector(pCore.manipMoveContext("Move", q=1, p=1))
pCore.setToolTo(currentTool)
loc = pCore.spaceLocator(n="Average#")
loc.setTranslation(pos)
normal = face.getNormal(space="world")
# reselect the face and get the perimeter edges
pCore.select(face)
pCore.mel.ConvertSelectionToEdgePerimeter()
edgeList = pCore.selected(fl=True)
# get the longest edges and pull out the world position of the start and end of that edge
lengths = []
for edge in edgeList:
lengths.append((edge.getLength(), edge))
longestEdge = sorted(lengths)[-1][1]
posA = longestEdge.getPoint(0, space="world")
posB = longestEdge.getPoint(1, space="world")
# Aim the locator using the some dummy locators
locTemp = pCore.spaceLocator(n="tmp#")
locTemp.setTranslation(posA)
locTempB = pCore.spaceLocator(n="tmp#")
locTempB.setTranslation(posB)
# Make sure the Z Axis is the Primary Axis pointing along the normal.
pCore.delete(pCore.aimConstraint(locTemp, locTempB, loc, aimVector=(1, 0, 0), upVector=(0, 0, -1), worldUpVector=normal))
pCore.delete([locTemp, locTempB])
if name:
loc.rename(name)
return loc
def rivetLocatorToSelectedPoly(name=""):
"""
Quick locator constrained to a Follicle. Must have a single face selected
:param name: str
:return: None
"""
face = pCore.selected()[0]
if len(face) == 1:
if isinstance(face, pCore.MeshFace):
rivetLoc = locatorOnPoly(name)
pCore.select(rivetLoc)
pCore.move((0, 0, -0.005), r=1, os=1) # moving off the surface doesn't improve.
# Why does getUVAtPoint not work consistently? This has started in Many scripts or even the closestPointOnMesh node
# Running it once will return [0,0]. Noticed that when I ran it several times it returns the correct value,
# eventually. I though perhaps it was choosing a random direction + or - if one isn't given.
u, v = [0, 0]
for x in range(0):
u, v = face.getUVAtPoint(rivetLoc.getRotatePivot(space="world"), "world", "map1")
if any([u, v]):
break
pCore.select([])
pCore.select([rivetLoc, face])
fTrans, fShape = createFollicle(rivetLoc.shortName(), poly_surface=face.node().name(), u=u, v=v)
pCore.parentConstraint(fTrans, rivetLoc, mo=True)
def createFollicle(name, nurbs_surface=None, poly_surface=None, u=0, v=0):
"""
Simple follicle creation wrapper.
:param name: str
:param nurbs_surface: str
:param poly_surface: str
:param u: int
:param v: int
:return: str(transform), str(follicleShape)
"""
# create a follicle node and connect it
follicle_transform = cmds.createNode("transform", n=(name + "follicle"))
follicle = cmds.createNode("follicle", n=(name + "follicleShape"), p=follicle_transform)
cmds.connectAttr((follicle + ".outTranslate"), (follicle_transform + ".translate"), f=1)
cmds.connectAttr((follicle + ".outRotate"), (follicle_transform + ".rotate"), f=1)
if nurbs_surface:
cmds.connectAttr((nurbs_surface + ".local"), (follicle + ".is"), f=1)
cmds.connectAttr((nurbs_surface + ".worldMatrix[0]"), (follicle + ".inputWorldMatrix"), f=1)
if poly_surface:
cmds.connectAttr((poly_surface + ".outMesh"), (follicle + ".inm"), f=1)
cmds.connectAttr((poly_surface + ".worldMatrix[0]"), (follicle + ".inputWorldMatrix"), f=1)
cmds.setAttr((follicle + ".parameterU"), u)
cmds.setAttr((follicle + ".parameterV"), v)
return follicle_transform, follicle
@AndresMWeber
Copy link

The problem with this code is specifying your world space as a string value. It requires an integer and for maximum future proofing you should use the pre-defined one from the api:
OpenMaya.MSpace.kWorld

Feed that into your second argument instead of "world" and assuming that you've iterated through the faces correctly, it should return the correct result.

@thirstydevil
Copy link
Author

Thanks Andres, I've updated the code so you can see the complete picture.

Regards

David

@AndresMWeber
Copy link

I'm happy to hear it somehow works now and thanks for sharing the code. Funny that the one method call has 3 different possible modules that it comes from, my mistake! I wrote an API 2.0 version just in case you're interested (I know this was an issue that came from just having to update some scripts so I'm sure it's not retroactively helpful, but maybe going forward.):
https://gist.github.com/AndresMWeber/27ff60f361627d8bed1fa962929c37b8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment