Sample Code

Sample code for python in Maya

# The geometry library for maya
# Use mathematical expression for geometrical shapes such as lines, surface, etc
# Class: line, plane, box, poly
# By Fred Qiao 2018


import maya.cmds as cmds
from functools import partial
import maya.mel as mel
import math
import string
import copy
import numpy as np
from adjacentPoly import *


def minusVerts(vert1, vert2):
	result = []
	if len(vert1) != len(vert2):
		print "minusVerts not same length"
		return None
	for i in xrange(len(vert1)):
		result += [vert1[i] - vert2[i]]
	return result



# lineFunc format: [[l, x1], [m, y1], [n, z1]]
class Line:
	def __init__(self, vert1, vert2):
		vec = minusVerts(vert1, vert2)
		lineFunc = []
		for i in xrange(len(vec)):
			lineFunc += [[vec[i], vert1[i]]]
		self.lineFunc = lineFunc
		self.l = vec[0]
		self.m = vec[1]
		self.n = vec[2]

		self.x = vert1[0]
		self.y = vert1[1]
		self.z = vert1[2]


	def getPointOnLine(self, value, axis):
		result = [0, 0, 0]
		if axis == "x":
			t = (value - self.lineFunc[0][1])/self.lineFunc[0][0]
			result[0] = value
			result[1] = t * self.lineFunc[1][0] + self.lineFunc[1][1]
			result[2] = t * self.lineFunc[2][0] + self.lineFunc[2][1]
		if axis == "y":
			t = (value - self.lineFunc[1][1])/self.lineFunc[1][0]
			result[1] = value
			result[0] = t * self.lineFunc[0][0] + self.lineFunc[0][1]
			result[2] = t * self.lineFunc[2][0] + self.lineFunc[2][1]
		if axis == "z":
			t = (value - self.lineFunc[2][1])/self.lineFunc[2][0]
			result[2] = value
			result[0] = t * self.lineFunc[0][0] + self.lineFunc[0][1]
			result[1] = t * self.lineFunc[1][0] + self.lineFunc[1][1]
		return result


	def leftOrRight(self, vertTest):
		pointOnLine = self.getPointOnLine(vertTest[2], "z")
		if vertTest[0] > pointOnLine[0]:
			return "R"
		else:
			return "L"


	def upOrDown(self, vertTest):
		pointOnLine = self.getPointOnLine(vertTest[0], "x")
		if vertTest[1] > pointOnLine[1]:
			return "U"
		else:
			return "D"

	def frontOrBack(self, vertTest):
		pointOnLine = self.getPointOnLine(vertTest[1], "y")
		if vertTest[2] > pointOnLine[2]:
			return "F"
		else:
			return "B"


# plane is an class with ax + by + cz = s
class Plane:

	# init function, starting from two lines
	def __init__(self, line1, line2):
		connectingLine = minusVerts([line2.x, line2.y, line2.z], [line1.x, line1.y, line1.z])
		crossProduct = np.cross(connectingLine, [line1.l, line1.m, line1.n])
		scalar = np.vdot (np.array(crossProduct), np.array([line2.x, line2.y, line2.z]))
		self.a = crossProduct[0]
		self.b = crossProduct[1]
		self.c = crossProduct[2]
		self.s = scalar

		# one arbitrary point on the plane
		self.point = [line1.x, line1.y, line1.z]


	# given 2 values, find the point on the plane
	def getPointOnPlane(self, value1, value2, axis):
		result = [0, 0, 0]
		if axis == "xy":
			result[0] = value1
			result[1] = value2
			result[2]= (self.s - self.a * value1 - self.b * value2)/self.c
		if axis == "yz":
			result[1] = value1
			result[2] = value2
			result[0]= (self.s - self.b * value1 - self.c * value2)/self.a
		if axis == "xz":
			result[0] = value1
			result[2] = value2
			result[1]= (self.s - self.a * value1 - self.c * value2)/self.b
		return result




	def leftOrRight(self, vertTest):
		pointOnPlane = self.getPointOnPlane(vertTest[1], vertTest[2], "yz")
		if vertTest[0] > pointOnPlane[0]:
			return "R"
		else:
			return "L"


	def frontOrBack(self, vertTest):
		pointOnPlane = self.getPointOnPlane(vertTest[0], vertTest[1], "xy")
		if vertTest[2] > pointOnPlane[2]:
			return "F"
		else:
			return "B"

	def upOrDown(self, vertTest):
		pointOnPlane = self.getPointOnPlane(vertTest[0], vertTest[2], "xz")
		if vertTest[1] > pointOnPlane[1]:
			return "U"
		else:
			return "D"

	def printVal(self):
		print self.a
		print "A: " + str(self.a)
		print "B: " + str(self.b)
		print "C: " + str(self.c)
		print "S: " + str(self.s)

# input: 2 planes and [x, y, z], 
# output if the vert is between the two plane
def inBetweenPlanes(plane1, plane2, vert):
	x = vert[0]
	y = vert[1]
	z = vert[2]

	px = plane2.point[0]
	py = plane2.point[1]
	pz = plane2.point[2]
	planeDist = (plane1.a * px) + (plane1.b * py) + (plane1.c * pz) + (-plane1.s)
	dotOne = (plane1.a * x) + (plane1.b * y) + (plane1.c * z) + (-plane1.s)

	# same sign, on the same side, different sign, on different sides
	return ((dotOne  * planeDist) > 0) and (abs(planeDist) > abs(dotOne))

def testPlaneAll(side, plane):
	print "test plane"
	selectionList = []
	plane.printVal()
	for i in xrange(288):
		cubeName = "pCube" + str(i+1)
		if (side == "U" or side == "D"):
			if plane.upOrDown(cmds.xform(cubeName, translation = True, worldSpace = True, query = True)) == side:
				selectionList += [cubeName]
		elif (side == "R" or side == "L"):
			if plane.leftOrRight(cmds.xform(cubeName, translation = True, worldSpace = True, query = True)) == side:
				selectionList += [cubeName]
		elif (side == "F" or side == "B"):
			if plane.frontOrBack(cmds.xform(cubeName, translation = True, worldSpace = True, query = True)) == side:
				selectionList += [cubeName]

	cmds.select(selectionList)


# the box is initiated by 3 pairs of planes, which are the faces of the boxes that oppsite with each other
class Box:
	def __init__(self, planeSet1, planeSet2, planeSet3, faceList, addType,
				 vertLTB, vertLTF, vertLBB, vertLBF, 
				 vertRTB, vertRTF, vertRBB, vertRBF):
		self.planeU = planeSet1[0]
		self.planeD = planeSet1[1]
		self.planeR = planeSet2[0]
		self.planeL = planeSet2[1]
		self.planeB = planeSet3[0]
		self.planeF = planeSet3[1]

		self.faceList = faceList
		self.vertList = faceToVerts(faceList)
		self.addType = addType

		# vertex order to compute the planes in the right order
		self.vertLTB = vertLTB
		self.vertLTF = vertLTF
		self.vertLBB = vertLBB
		self.vertLBF = vertLBF
		self.vertRTB = vertRTB
		self.vertRTF = vertRTF
		self.vertRBB = vertRBB
		self.vertRBF = vertRBF

	
	def checkFace(self, face):
		return (face in self.faceList)


	def insideBox(self, vertTest):
		if (inBetweenPlanes(self.planeU, self.planeD, vertTest) and
		    inBetweenPlanes(self.planeF, self.planeB, vertTest) and
		    inBetweenPlanes(self.planeL, self.planeR, vertTest)):
		# if (self.planeU.upOrDown(vertTest) != self.planeD.upOrDown(vertTest) and 
		# 	self.planeL.leftOrRight(vertTest) != self.planeR.leftOrRight(vertTest) and 
		# 	self.planeF.frontOrBack(vertTest) != self.planeB.frontOrBack(vertTest)):
			return True
		else:
			return False

	def updateMath(self):
		meshVertList = polyToIndex(self.vertList)
		print meshVertList
		mesh = polyToTrans(self.vertList[0])
		# sort positions of vertices in x, y and z directions

		# create box
		posList = [-1]
		posList += [cmds.xform(self.vertRTF, query = True, translation = True, worldSpace = True)]
		posList += [cmds.xform(self.vertRTB, query = True, translation = True, worldSpace = True)]
		posList += [cmds.xform(self.vertLTB, query = True, translation = True, worldSpace = True)]
		posList += [cmds.xform(self.vertLTF, query = True, translation = True, worldSpace = True)]
		posList += [cmds.xform(self.vertLBF, query = True, translation = True, worldSpace = True)]
		posList += [cmds.xform(self.vertLBB, query = True, translation = True, worldSpace = True)]
		posList += [cmds.xform(self.vertRBB, query = True, translation = True, worldSpace = True)]
		posList += [cmds.xform(self.vertRBF, query = True, translation = True, worldSpace = True)]
		print posList


		# up and down faces
		line1 = Line(posList[1], posList[2])
		line2 = Line(posList[3], posList[4])
		self.planeU = Plane(line1, line2)
		

		line3 = Line(posList[5], posList[6])
		line4 = Line(posList[7], posList[8])
		self.planeD = Plane(line3, line4)

		# left and right faces
		self.planeR = Plane(line1, line4)
		self.planeL = Plane(line2, line3)


		# front and back faces
		line5 = Line(posList[2], posList[3])
		line6 = Line(posList[6], posList[7])
		self.planeB = Plane(line5, line6)

		line7 = Line(posList[1], posList[4])
		line8 = Line(posList[5], posList[8])
		self.planeF = Plane(line7, line8)
		return None
	

	def printVal(self):
		print " "
		print "begin - box info:"
		print "U: "
		self.planeU.printVal()
		print "D:"
		self.planeD.printVal()
		print "R: "
		self.planeR.printVal()
		print "L: "
		self.planeL.printVal()
		print "B: "
		self.planeB.printVal()
		print "F: "
		self.planeB.printVal()
		print "end - boxinfo"
		print " "


# the function to create a box object from maya mesh
def createBox(mesh, meshVertList, meshFaceList, addType):
	print mesh
	print meshVertList
	print meshFaceList
	print addType

	# sort positions of vertices in x, y and z directions
	xList = copy.deepcopy(meshVertList)
	yList = copy.deepcopy(meshVertList)
	zList = copy.deepcopy(meshVertList)
	positionList = []
	for i in meshVertList:
		meshVert = mesh + ".vtx[" + str(i) + "]"
		xList[i] = (xList[i], cmds.xform(meshVert, query = True, translation = True))
		yList[i] = (yList[i], cmds.xform(meshVert, query = True, translation = True))
		zList[i] = (zList[i], cmds.xform(meshVert, query = True, translation = True))
	xList.sort(key=lambda x: x[1][0])
	yList.sort(key=lambda x: x[1][1])
	zList.sort(key=lambda x: x[1][2])
	print xList
	print yList
	print zList

	for i in meshVertList:
		meshVert = mesh + ".vtx[" + str(i) + "]"
		xList[i] = xList[i][0]
		yList[i] = yList[i][0]
		zList[i] = zList[i][0]
	# assign init value to 8 vertices of the box
	vertLTB = -1
	vertLTF = -1
	vertLBB = -1
	vertLBF	= -1
	vertRTB = -1
	vertRTF = -1
	vertRBB = -1
	vertRBF = -1

	# find position of each vertex
	for i in meshVertList:
		vertName = mesh+".vtx[" + str(i) + "]"
		if (i in xList[0:4]):
			if (i in yList[0:4]):
				if (i in zList[0:4]):
					vertLBB = vertName
				else:
					vertLBF = vertName
			else:
				if (i in zList[0:4]):
					vertLTB = vertName
				else:
					vertLTF = vertName
		else:
			if (i in yList[0:4]):
				if (i in zList[0:4]):
					vertRBB = vertName
				else:
					vertRBF = vertName
			else:
				if (i in zList[0:4]):
					vertRTB = vertName
				else:
					vertRTF = vertName

	# create box
	posList = [-1]
	posList += [cmds.xform(vertRTF, query = True, translation = True, worldSpace = True)]
	posList += [cmds.xform(vertRTB, query = True, translation = True, worldSpace = True)]
	posList += [cmds.xform(vertLTB, query = True, translation = True, worldSpace = True)]
	posList += [cmds.xform(vertLTF, query = True, translation = True, worldSpace = True)]
	posList += [cmds.xform(vertLBF, query = True, translation = True, worldSpace = True)]
	posList += [cmds.xform(vertLBB, query = True, translation = True, worldSpace = True)]
	posList += [cmds.xform(vertRBB, query = True, translation = True, worldSpace = True)]
	posList += [cmds.xform(vertRBF, query = True, translation = True, worldSpace = True)]

	# up and down faces
	line1 = Line(posList[1], posList[2])
	line2 = Line(posList[3], posList[4])
	planeA1 = Plane(line1, line2)

	line3 = Line(posList[5], posList[6])
	line4 = Line(posList[7], posList[8])
	planeA2 = Plane(line3, line4)

	# left and right faces
	planeB1 = Plane(line1, line4)
	planeB2 = Plane(line2, line3)


	# front and back faces
	line5 = Line(posList[2], posList[3])
	line6 = Line(posList[6], posList[7])
	planeC1 = Plane(line5, line6)

	line7 = Line(posList[1], posList[4])
	line8 = Line(posList[5], posList[8])
	planeC2 = Plane(line7, line8)

	faceList = []
	for i in meshFaceList :
		faceList += [mesh + ".f[" + str(i) + "]"]

	return Box([planeA1, planeA2], [planeB1, planeB2], [planeC1, planeC2], 
				faceList, addType, 
				vertLTB, vertLTF, vertLBB, vertLBF, 
				vertRTB, vertRTF, vertRBB, vertRBF)

# the function to create a box from maya mesh
def getBoxFromView(addType = "additive"):
	mesh = cmds.ls(selection = True)[0]
	meshFaceList = range(8)
	meshVertList = range(8)
	return createBox(mesh, meshVertList, meshFaceList, addType)

def getBoxFromMesh(mesh, addType = "additive"):
	meshFaceList = range(8)
	meshVertList = range(8)
	return createBox(mesh, meshVertList, meshFaceList, addType)



# polygon object has 2 features, one is a dictionary, the other is the number of levels.
# poly is a type that interact with meshes
# Each polygon is eather a plane or another polygon
class Poly:
	def __init__(self, box):
		# The list of boxes
		self.boxList = [box]
		faceList = box.faceList
		meshName = polyToTrans(faceList[0])
		# dictionary of boxes, keys are mesh names
		self.boxDict = dict([])
		self.boxDict[meshName] = box
		# number of boxes in poly
		self.boxNum = 1

	# first iterate through all levels, and check each box's inside objects.
	# vert lists are the string names
	# If they are in the previous ones, delete them, if not, add them
	def insidePoly(self, vertList):
		chosenVertList = []
		boxList = self.boxList
		# print vertList
		for meshVert in vertList:
			vert = cmds.xform(meshVert, query = True, translation = True, worldSpace = True)
			record = False
			for i in xrange(len(boxList)):
				box = boxList[i]
				# print "before call insidebox"
				if box.insideBox(vert):
					# print "success call insidebox"
					# print box.addType
					if box.addType == "additive":
						record = True
					else:
						record = False
					# print record
			if record:
				chosenVertList += [meshVert]
		return chosenVertList

	# given the extended face and the new face list, update the poly with
	# new box
	def addBox(self, mesh, box):
		self.boxDict[mesh] = box
		self.boxList += [box]
		self.boxNum += 1
		# update the old box attributes


	# given the faceList, update the polygon's box whose keys are the faces in the list
	def updateBox(self, mesh):
		box = self.boxDict[mesh]
		box.updateMath()

# wrapper function to create poly from selection
def getPolyFromView():
	mesh = cmds.ls(selection = True)[0]
	meshFaceList = range(8)
	meshVertList = range(8)
	box = createBox(mesh, meshVertList, meshFaceList, "additive")
	return Poly(box)

# wrapper function to create poly from a mesh name
def getPolyFromMesh(mesh):
	meshFaceList = range(8)
	meshVertList = range(8)
	box = createBox(mesh, meshVertList, meshFaceList, "additive")
	return Poly(box)



# test functions for lines
def getLinePos():
	crv = cmds.ls(selection = True)[0]
	pos1 = cmds.xform(crv+".cv[0]", query = True, translation = True, worldSpace = True)
	pos2 = cmds.xform(crv+".cv[1]", query = True, translation = True, worldSpace = True)
	return [pos1, pos2]


def testLR(side):
	selectionList = []
	for i in xrange(288):
		cubeName = "pCube" + str(i+1)
		line = Line(vertList[0], vertList[1])
		if line.leftOrRight(cmds.xform(cubeName, translation = True, worldSpace = True, query = True)) == side:
			selectionList += [cubeName]
	cmds.select(selectionList)

def testUD(side):
	selectionList = []
	for i in xrange(288):
		cubeName = "pCube" + str(i+1)
		line = Line(vertList[0], vertList[1])
		if line.upOrDown(cmds.xform(cubeName, translation = True, worldSpace = True, query = True)) == side:
			selectionList += [cubeName]
	cmds.select(selectionList)

def testFB(side):
	selectionList = []
	for i in xrange(288):
		cubeName = "pCube" + str(i+1)
		line = Line(vertList[0], vertList[1])
		if line.frontOrBack(cmds.xform(cubeName, translation = True, worldSpace = True, query = True)) == side:
			selectionList += [cubeName]
	cmds.select(selectionList)

def getPlaneFromView():
	crv = cmds.ls(selection = True)[0]
	pos1 = cmds.xform(crv+".cv[0]", query = True, translation = True, worldSpace = True)
	pos2 = cmds.xform(crv+".cv[1]", query = True, translation = True, worldSpace = True)
	line1 = Line(pos1, pos2)

	pos3 = cmds.xform(crv+".cv[2]", query = True, translation = True, worldSpace = True)
	pos4 = cmds.xform(crv+".cv[3]", query = True, translation = True, worldSpace = True)
	line2 = Line(pos3, pos4)

	plane = Plane(line1, line2)
	plane.printVal
	return plane



def testUDPlaneOne(side):
	selectionList = []
	plane = getPlaneFromView()
	plane.printVal()
	cubeName = "pCube289"
	result = plane.upOrDown(cmds.xform(cubeName, translation = True, worldSpace = True, query = True))
	print result
	if  result == side:
		cmds.select(selectionList)



def testBoxAll(box):
	selectionList = []
	for i in xrange(288):
		cubeName = "pCube" + str(i+1)
		if box.insideBox(cmds.xform(cubeName, translation = True, worldSpace = True, query = True)):
			selectionList += [cubeName]
	cmds.select(selectionList)

def testBoxOne(box):
	selectionList = []
	cubeName = "pCube289"
	if box.insideBox(cmds.xform(cubeName, translation = True, worldSpace = True, query = True)):
		selectionList += [cubeName]

def testPolyAll(poly):
	selectionList = []
	box = getBoxFromView()
	for i in xrange(288):
		cubeName = "pCube" + str(i+1)
		if box.insideBox(cmds.xform(cubeName, translation = True, worldSpace = True, query = True)):
			selectionList += [cubeName]
	cmds.select(selectionList)