def inflateCurveOnMesh(crv, mesh, scale=1): newCrv = pm.duplicate(crv)[0] sel_list = om.MSelectionList() sel_list.add(str(crv)) sel_list.add(str(mesh)) sel_list.add(str(newCrv)) DagPath, _ = sel_list.getComponent(0) Curve = om.MFnNurbsCurve(DagPath) CurveCVs = Curve.cvPositions(om.MSpace.kWorld) newCurveCVs = om.MPointArray() DagPath, _ = sel_list.getComponent(1) Mesh = om.MFnMesh(DagPath) DagPath, _ = sel_list.getComponent(2) newCurve = om.MFnNurbsCurve(DagPath) for i in range(len(CurveCVs)): cv = CurveCVs[i] normal, _ = Mesh.getClosestNormal(cv) x = cv.x + normal.x * scale y = cv.y + normal.y * scale z = cv.z + normal.z * scale newCurveCV = om.MPoint(x, y, z) newCurveCVs.append(newCurveCV) newCurve.setCVPositions(newCurveCVs) return newCrv
def mirrorCurveCvs(curveObj, axis="x", space=None): """Mirrors the the curves transform shape cvs by a axis in a specified space :param curveObj: The curves transform to mirror :type curveObj: mobject :param axis: the axis the mirror on, accepts: 'x', 'y', 'z' :type axis: str :param space: the space to mirror by, accepts: MSpace.kObject, MSpace.kWorld, default: MSpace.kObject :type space: int Example:: >>>nurbsCurve = cmds.circle()[0] >>>mirrorCurveCvs(api.asMObject(nurbsCurve), axis='y', space=om.MSpace.kObject) """ space = space or om2.MSpace.kObject axis = axis.lower() axisDict = {'x': 0, 'y': 1, 'z': 2} axis = axisDict[axis] shapes = nodes.shapes(om2.MFnDagNode(curveObj).getPath()) for shape in shapes: curve = om2.MFnNurbsCurve(shape) cvs = curve.getCVs(space=space) # invert the cvs MPoints based on the axis for i in range(len(cvs)): cvs[i][axis] *= -1 curve.setCvPositions(cvs) curve.updateCurve()
def find_point_on_curve_to_point_by_distance(curve, to_point, distance, error_threshold = 1.0): sl = om.MSelectionList() sl.add(curve) curve_fn = om.MFnNurbsCurve(sl.getDagPath(0)) u_begin = curve_fn.getParamAtPoint(om.MPoint(to_point), space = om.MSpace.kWorld) max_param = curve_fn.numCVs - curve_fn.degree arc_length = curve_fn.findLengthFromParam(max_param) u_increase = error_threshold / arc_length param_begin = u_begin * max_param p_ = curve_fn.getPointAtParam(param_begin, space = om.MSpace.kWorld) u_ = u_begin while u_ < 1: u_param = u_ * max_param p = curve_fn.getPointAtParam(u_param, space = om.MSpace.kWorld) length = (p - p_).length() if abs(length - distance) <= error_threshold: return p, u_ u_ += u_increase return p, 1.0
def getCurveData(shape, space=om2.MSpace.kObject): """From a given NurbsCurve shape node serialize the cvs positions, knots, degree, form rgb colours :param shape: MObject that represents the NurbsCurve shape :return: dict :param space: :type space: om2.MSpace Example:: >>>nurbsCurve = cmds.circle()[1] # requires an MObject of the shape node >>>data = curve_utils.getCurveData(api.asMObject(nurbsCurve)) """ if isinstance(shape, om2.MObject): shape = om2.MFnDagNode(shape).getPath() data = nodes.getNodeColourData(shape.node()) curve = om2.MFnNurbsCurve(shape) # so we can deserialize in world which maya does in to steps if space == om2.MSpace.kWorld: data["matrix"] = list(nodes.getWorldMatrix(curve.object())) data.update({ "knots": tuple(curve.knots()), "cvs": map(tuple, curve.cvPositions(space)), "degree": curve.degree, "form": curve.form }) return data
def iterCurvePoints(dagPath, count, space=om2.MSpace.kObject): """Generator Function to iterate and return the position, normal and tangent for the curve with the given point count. :param dagPath: the dagPath to the curve shape node :type dagPath: om2.MDagPath :param count: the point count to generate :type count: int :param space: the coordinate space to query the point data :type space: om2.MSpace :return: The first element is the Position, second is the normal, third is the tangent :rtype: tuple(MVector, MVector, MVector) """ crvFn = om2.MFnNurbsCurve(dagPath) length = crvFn.length() dist = length / float(count - 1) # account for end point current = 0.001 maxParam = crvFn.findParamFromLength(length) for i in xrange(count): param = crvFn.findParamFromLength(current) # maya fails to get the normal when the param is the maxparam so we sample with a slight offset if param == maxParam: param = maxParam - 0.0001 point = om2.MVector(crvFn.getPointAtParam(param, space=space)) yield point, crvFn.normal(param, space=space), crvFn.tangent(param, space=space) current += dist
def compute(self, plug, dataBlock): if plug not in [self.outTranslate]: self.displayWarning(error='Unknown plug: {}'.format(plug)) return curveHandle = dataBlock.inputValue(self.inputCurve) curveData = curveHandle.data() if curveData.isNull(): return curveFn = om.MFnNurbsCurve(curveData) fractionMode = dataBlock.inputValue(self.fractionMode).asBool() if fractionMode: uValueMult = curveFn.length() else: uValueMult = 1.0 output_arrayHandle = dataBlock.outputArrayValue(self.output) output_builder = output_arrayHandle.builder() uValueArrayHandle = dataBlock.inputArrayValue(self.uValue) for i in range(len(uValueArrayHandle)): uValueArrayHandle.jumpToPhysicalElement(i) index = uValueArrayHandle.elementLogicalIndex() outputHandle = output_builder.addElement(index) outTranslateHandle = outputHandle.child(self.outTranslate) uValue = uValueArrayHandle.inputValue().asFloat() * uValueMult parameter = curveFn.findParamFromLength(uValue) outTranslate = curveFn.getPointAtParam(parameter, space=om.MSpace.kWorld) outTranslateHandle.set3Float(outTranslate[0], outTranslate[1], outTranslate[2]) output_arrayHandle.set(output_builder) output_arrayHandle.setAllClean() dataBlock.setClean(plug)
def setScale(self, scale, cvs=False, space=None): """Applies the specified scale vector to the transform or the cvs :param space: the space to work on, eg. MSpace.kWorld :type scale: sequence :type space: int :param cvs: if True then the scaling vector will be applied to the cv components :type cvs: bool """ if self.dagPath is None: return space = space or om2.MSpace.kObject if cvs: shapes = nodes.shapes(self.dagPath) for shape in shapes: curve = om2.MFnNurbsCurve(shape) positions = curve.cvPositions(space) newPositions = om2.MPointArray() for i in positions: newPositions.append( om2.MPoint(i[0] * scale[0], i[1] * scale[1], i[2] * scale[2])) nodes.setCurvePositions(shape.node(), newPositions, space=space) return trans = om2.MFnTransform(self.dagPath) trans.setScale(scale)
def linspace_bones(self, curve, bones): ''' Provide matrices that are an equal distribution along a curve Args: curve (str): name of curve to get information from bones (list[str]): number of controls to be created for up vec of curve ''' sel = om.MSelectionList() sel.add(curve) crv = om.MFnNurbsCurve() crv.setObject(sel.getDagPath(0)) curve_length = crv.length() lengths = np.linspace(0, curve_length, len(bones)) # create instance to control pos and rot of controls being attached for i, length in enumerate(lengths): bone = bones[i] poci = cmds.createNode('pointOnCurveInfo', name=bone + '_POCI') param = crv.findParamFromLength(length) if param == 0: param = .001 if length == lengths[-1]: param -= .001 cmds.connectAttr(curve + '.worldSpace[0]', poci + '.inputCurve') cmds.setAttr(poci + '.parameter', param) cmds.connectAttr(poci + '.position', bone + '.translate')
def iterCurvePoints(dagPath, count, space=om2.MSpace.kObject): """Generator Function to iterate and return the position, normal and tangent for the curve with the given point count. :param dagPath: the dagPath to the curve shape node :type dagPath: om2.MDagPath :param count: the point count to generate :type count: int :param space: the coordinate space to query the point data :type space: om2.MSpace :return: The first element is the Position, second is the normal, third is the tangent :rtype: tuple(MVector, MVector, MVector) """ crvFn = om2.MFnNurbsCurve(dagPath) length = crvFn.length() dist = length / float(count - 1) # account for end point current = 0.001 maxParam = crvFn.findParamFromLength(length) defaultNormal = [1.0, 0.0, 0.0] defaultTangent = [0.0, 1.0, 0.0] for i in xrange(count): param = crvFn.findParamFromLength(current) # maya fails to get the normal when the param is the maxparam so we sample with a slight offset if param == maxParam: param = maxParam - 0.0001 point = om2.MVector(crvFn.getPointAtParam(param, space=space)) # in case where the curve is flat eg. directly up +y # this causes a runtimeError in which case the normal is [1.0,0.0,0.0] and tangent [0.0,1.0,0.0] try: yield point, crvFn.normal(param, space=space), crvFn.tangent(param, space=space) except RuntimeError: yield point, defaultNormal, defaultTangent current += dist
def create(self, transform): """Create the maya curve.""" curve = om2.MFnNurbsCurve() self.mobject = curve.create( self.cvs, self.knots, self.degree, self.form, False, False, transform, ) name = curve.name() cmds.setAttr("{}.overrideRGBColors".format(name), self.overrideRGBColors) cmds.setAttr( "{}.overrideColorRGB".format(name), self.overrideColorRGB.r, self.overrideColorRGB.g, self.overrideColorRGB.b, ) cmds.setAttr("{}.overrideEnabled".format(name), self.overrideEnabled) cmds.setAttr("{}.useOutlinerColor".format(name), self.useOutlinerColor) cmds.setAttr( "{}.outlinerColor".format(name), self.outlinerColor.r, self.outlinerColor.g, self.outlinerColor.b, )
def initilizeStiffness(self, dataBlock): """ sets up the default stiffness params """ inCurveDataHandle = dataBlock.inputValue(self.inCurve) mCurve = inCurveDataHandle.asNurbsCurveTransformed() if not mCurve: return # stiffness mfnCrv = om.MFnNurbsCurve(mCurve) stiffnessArrayData = dataBlock.outputArrayValue(self.stiffness) stiffnessDataBuilder = om.MArrayDataBuilder(dataBlock, self.stiffness, int(mfnCrv.numCVs)) # outputs outputArrayData = dataBlock.outputArrayValue(self.output) outputDataBuilder = om.MArrayDataBuilder(dataBlock, self.output, int(mfnCrv.numCVs)) for i in xrange(mfnCrv.numCVs): stiffnessHandler = stiffnessDataBuilder.addElement(i) stiffnessHandler.setFloat(0.5) stiffnessHandler.setClean() outputHandler = outputDataBuilder.addElement(i) outputHandler.setMVector(om.MVector(0, 0, 0)) outputHandler.setClean() stiffnessArrayData.set(stiffnessDataBuilder) outputArrayData.set(outputDataBuilder) setInitMode = om.MPlug(self.thisMObject(), self.initilize) setInitMode.setShort(2)
def makeCurvFromPoints(cvs_array, transform_fn=None): """ Generate a spline from an array of points. :param cvs_array: MPointArray to generate curve from. :param transform_fn: Optional argument for parenting curve under existing transform. :return: TransformFn, NurbsCurveFn """ knot_vector = om2.MDoubleArray() # calculate knot vector degree = 2 nspans = len(cvs_array) - degree nknots = nspans + 2 * degree - 1 for i in range(degree - 1): knot_vector.append(0.0) for j in range(nknots - (2 * degree) + 2): knot_vector.append(j) for k in range(degree - 1): knot_vector.append(j) # create curve if transform_fn is None: transform_fn = om2.MFnTransform() transform_fn.create() # transform_fn.setName('%s_input_curv' % name) curvFn = om2.MFnNurbsCurve() curvFn.create(cvs_array, knot_vector, degree, om2.MFnNurbsCurve.kOpen, False, True, transform_fn.object()) return transform_fn, curvFn
def getPointOnCurveFromPosition(curve, point, space=om.MSpace.kWorld): ''' Gets a curves point at curve from position. It will return a MPoint :param curve: Curve you want to get paremter for :type curve: *str* or *MObject* :param point: Point in space or Node to get MPoint from :type: list | MPoint :return: This will return the position in world space on the curve :rtype: MPoint ''' #get dag path for curve and assign it a nurbsCurve function object dagPath = rigrepo.libs.transform.getDagPath(curve) dagPath.extendToShape() mFnNurbsCurve = om.MFnNurbsCurve(dagPath) #Check to see if point is a list or tuple object if not isinstance(point, list) and not isinstance(point, tuple): if mc.objExists(point): point = mc.xform(point, q=True, ws=True, t=True) return mFnNurbsCurve.closestPoint(om.MPoint(point[0], point[1], point[2]), space=space)[0]
def compute(self, plug, data): if plug == Node.aOutCurve: matricesHandle = data.inputArrayValue(Node.aMatrices) curveHandle = data.outputValue(Node.aOutCurve) points = om.MPointArray() while True: mat = matricesHandle.inputValue().asMatrix() p = om.MPoint(0, 0, 0, 1) points.append(p*mat) p = om.MPoint(0, 0.1, 0, 1) points.append(p*mat) if not matricesHandle.next(): break print points curveDataCreator = om.MFnNurbsCurveData() curveData = curveDataCreator.create() curveFn = om.MFnNurbsCurve() curve = curveFn.create( points, range(len(points)), 1, om.MFnNurbsCurve.kOpen, False, False, curveData) curveHandle.setMObject(curveData) data.setClean(plug)
def bind(self, dataBlock, curveFn, internalCurveData): """extends current curve start back along its tangent, ensuring a static base to the curve regardless of user modification waste a small amount of computation, but it's trivial""" # get tangent at start of curve in order to extend properly basePoint, baseTan = curveFn.getDerivativesAtParam(0) # MPoint, MVector curveCvs = curveFn.cvPositions() # MPointArray of starting points curveDegree = curveFn.degree # eventually take this into account for point generation curveSpans = curveFn.numSpans # add new points to cv array in direction of tangent addCvs = [] for i in range(2 * curveDegree): addCvs.insert(0, basePoint + baseTan * -((i + 1) * 0.1)) # MVectors addPoints = om.MPointArray() for i in addCvs: point = om.MPoint(i) addPoints.append(point) newCvs = addPoints + curveCvs # knots curveKnots = curveFn.knots() # MFloatArray targetLen = (curveSpans + 2 * curveDegree - 1) # print "current knots are {}".format(curveKnots) # print "current number knots is {}".format(len(curveKnots)) # print "target number is {}".format(targetLen) # add 4 to existing array, and insert [0,0,0,1,2,3] at beginning for i in range(len(curveKnots)): curveKnots[i] += float(2 * curveDegree) for i in range(curveDegree): # chop off first zeroes curveKnots.remove(0) # for i in [3.0,2.0,1.0,0.0,0.0]: # curveKnots.insert(i, 0) endKnots = [0.0 for i in range(curveDegree)] # print "endZeroes are {}".format(endKnots) for i in range(2 * curveDegree): endKnots.insert(0, float(i + 1)) for i in endKnots: curveKnots.insert(i, 0) # print "new knots are {}".format(curveKnots) # print "new length is {}".format(len(curveKnots)) # we now have new points extending away from base of curve # second half of these will move with tangent, first half will # stay static test = om.MFnNurbsCurve() return test.create(newCvs, curveKnots, 3, 1, False, False, parent=internalCurveData.object())
def uniformPointsOnCurve(crv, count): ''' Returns evenly spaced world points along the given curve. ''' crvFn = OpenMaya.MFnNurbsCurve( capi.asDagPath(crv) ) step = 1.0 / (count - 1) points = [ _getPoint(crvFn, step * i) for i in range(count) ] return points
def curveCvs(dagPath, space=om2.MSpace.kObject): """Generator Function to iterate and return the position, normal and tangent for the curve with the given point count. :param dagPath: the dagPath to the curve shape node :type dagPath: om2.MDagPath :param space: the coordinate space to query the point data :type space: om2.MSpace :return: The first element is the Position, second is the normal, third is the tangent :rtype: tuple(om2.MPoint) """ return om2.MFnNurbsCurve(dagPath).cvPositions(space=space)
def cluster_curve(curve, prefix=""): curve_shape_node = cmds.listRelatives(curve, s=True)[0] mfn_curve = om2.MFnNurbsCurve(get_mobject(curve_shape_node)) clusters = [] for i in xrange(mfn_curve.numCVs): cv = '{crv}.cv[{index}]'.format(crv=mfn_curve.partialPathName(), index=i) shape, handle = cmds.cluster(cv) handle = cmds.rename(handle, '{}Cluster{}_hdl'.format(prefix, i)) clusters.append(handle) return clusters
def getParam(pt=[0, 0, 0], crv=None, tol=0.001): if crv is None: return point = om.MPoint(pt[0], pt[1], pt[2]) curveFn = om.MFnNurbsCurve(getDag(crv)) isOnCurve = curveFn.isPointOnCurve(point) if isOnCurve: return curveFn.getParamAtPoint(point, tol, om.MSpace.kObject) else: return curveFn.closestPoint(point, tol, om.MSpace.kObject)[1]
def get_u_param(point, curve): curve_shape = cmds.listRelatives(curve, s=True)[0] curve_mobject = get_dag_path(curve_shape) m_point = om2.MPoint(point) mfn_curve = om2.MFnNurbsCurve(curve_mobject) if mfn_curve.isPointOnCurve(m_point): parameter = mfn_curve.getParamAtPoint(m_point, space=om2.MSpace.kWorld) else: closest_point, parameter = mfn_curve.closestPoint( m_point, space=om2.MSpace.kWorld) return parameter
def align_primgen_to_surface(target_obj=None): """ :return: """ if not target_obj or not cmds.objExists(target_obj): cmds.warning("Invalid target geo given...") return source_obj = om.MGlobal.getActiveSelectionList() target_obj_sl = om.MSelectionList() target_obj_sl.add(target_obj) target_path = target_obj_sl.getDagPath(0) up_vec = None for i in xrange(0, source_obj.length()): source_path = source_obj.getDagPath(i) if source_path.apiType() == 110: source_path_shape = source_path source_path_shape.extendToShape() if source_path_shape.apiType() == 267: mFn_mesh = om.MFnMesh(target_path) mFn_curve = om.MFnNurbsCurve(source_path) root_cv_p = mFn_curve.cvPosition(0) if root_cv_p: closest_n = mFn_mesh.getClosestNormal(root_cv_p) if closest_n: up_vec = om.MVector(closest_n[0]) if up_vec: conn = cmds.listConnections( source_path_shape.partialPathName() + '.worldSpace', d=True, s=False) if conn: cmds.setAttr(conn[0] + '.firstUpVecX', up_vec[0]) cmds.setAttr(conn[0] + '.firstUpVecY', up_vec[1]) cmds.setAttr(conn[0] + '.firstUpVecZ', up_vec[2])
def linspace_curve(curve, num): ''' Provide matrices that are an equal distribution along a curve Args: curve (str): name of curve to get information from num (int): number of matrices to be returned Return: list[om.MMatrix()]: list of matrices ''' sel = om.MSelectionList() sel.add(curve) crv = om.MFnNurbsCurve() crv.setObject(sel.getDagPath(0)) curve_length = crv.length() lengths = np.linspace(0, curve_length, num) matrices = [] mscs = [] for length in lengths: poci = cmds.createNode('pointOnCurveInfo') cm = cmds.createNode('composeMatrix') msc = cmds.createNode('millSimpleConstraint') mscs.append(msc) param = crv.findParamFromLength(length) if param == 0: param = .001 if length == lengths[-1]: param -= .001 cmds.connectAttr(curve + '.worldSpace[0]', poci + '.inputCurve') cmds.setAttr(poci + '.parameter', param) cmds.connectAttr(poci + '.position', cm + '.inputTranslate') cmds.connectAttr(cm + '.outputMatrix', msc + '.inMatrix') aim_vec = crv.tangent(param) side_vec = crv.normal(param) up_vec = aim_vec ^ side_vec pos = crv.getPointAtParam(param) mtx = maths.vectors_to_matrix(row1=aim_vec.normal(), row2=up_vec.normal(), row3=side_vec.normal(), row4=pos) matrices.append(mtx) return matrices, mscs
def ctrLoadJson(typeController, name, path, SFactor=1, ColorIndex=4): """ Load saved controllers from json Args: typeController: controller name name: file name path: path of file SFactor: scale factor ColorIndex: color index Returns: fullPathName of createdController, transform matrix """ controllerFile = ('%s/%s_controllers.json' % (path, name)) # load json with open(controllerFile, 'r') as f: # json to dictionary controllerDict = json.load(f) # list with controller parameters ctrParameters = controllerDict[typeController][0] transform = OpenMaya.MObject() for n, ctrParam in enumerate(ctrParameters): curveFn = OpenMaya.MFnNurbsCurve() # create controller form = curveFn.kOpen if ctrParam[3] == 0 else curveFn.kPeriodic # multiplyFactor CVPoints = [(i[0] * SFactor, i[1] * SFactor, i[2] * SFactor) for i in ctrParam[0]] # create curve curveFn.create( CVPoints, ctrParam[1], ctrParam[2], form, False, True, transform if isinstance( transform, OpenMaya.MObject) else transform.node()) # set color enhableColorsPlug = curveFn.findPlug('overrideEnabled', False) enhableColorsPlug.setBool(True) colorPlug = curveFn.findPlug('overrideColor', False) colorPlug.setInt(ColorIndex) newControllerDagPath = OpenMaya.MDagPath.getAPathTo( curveFn.object()) if n == 0: transform = OpenMaya.MDagPath.getAPathTo( newControllerDagPath.transform()) # return controller name, and saved matrix transform return transform.fullPathName(), controllerDict[typeController][1]
def iterCurveParams(dagPath, count): """Generator Function to iterate and return the Parameter :param dagPath: the dagPath to the curve shape node :type dagPath: om2.MDagPath :param count: the Number of params to loop :type count: int :return: The curve param value :rtype: float """ crvFn = om2.MFnNurbsCurve(dagPath) length = crvFn.length() dist = length / float(count - 1) # account for end point current = 0.001 for i in xrange(count): yield crvFn.findParamFromLength(current) current += dist
def createCurveShape(parent, data): """Create a specified nurbs curves based on the data :param parent: The transform that takes ownership of the shapes, if None is supplied then one will be created :type parent: MObject :param data: {"shapeName": {"cvs": [], "knots":[], "degree": int, "form": int, "matrix": []}} :type data: dict :return: A 2 tuple the first element is the MObject of the parent and the second is a list / of mobjects represents the shapes created. :rtype: tuple(MObject, list(MObject)) """ parentInverseMatrix = om2.MMatrix() if parent is None: parent = om2.MObject.kNullObj elif parent != om2.MObject.kNullObj: parentInverseMatrix = nodes.getWorldInverseMatrix(parent) newCurve = om2.MFnNurbsCurve() newShapes = [] for shapeName, curveData in iter(data.items()): cvs = om2.MPointArray( curveData["cvs"] ) # om2 allows a list of lists which converts to om2.Point per element knots = curveData["knots"] degree = curveData["degree"] form = curveData["form"] enabled = curveData["overrideEnabled"] matrix = curveData.get("matrix") if matrix is not None: mat = om2.MMatrix(matrix) for i in range(len(cvs)): cvs[i] *= mat * parentInverseMatrix shape = newCurve.create(cvs, knots, degree, form, False, False, parent) newShapes.append(shape) if parent == om2.MObject.kNullObj and shape.apiType( ) == om2.MFn.kTransform: parent = shape if enabled: plugs.setPlugValue(newCurve.findPlug("overrideEnabled", False), int(curveData["overrideEnabled"])) colours = curveData["overrideColorRGB"] outlinerColour = curveData.get("outlinerColor") nodes.setNodeColour(newCurve.object(), colours, outlinerColour=outlinerColour) return parent, newShapes
def createCurveShape(parent, data): """Create a specified nurbs curves based on the data :param parent: The transform that takes ownership of the shapes, if None is supplied then one will be created :type parent: MObject :param data: {"shapeName": {"cvs": [], "knots":[], "degree": int, "form": int, "matrix": []}} :type data: dict :return: the parent node :rtype: MObject """ if parent is None: parent = om2.MObject.kNullObj newCurve = om2.MFnNurbsCurve() cvData = [] for shapeName, curveData in iter(data.items()): cvs = om2.MPointArray( curveData["cvs"] ) # om2 allows a list of lists which converts to om2.Point per element knots = curveData["knots"] degree = curveData["degree"] form = curveData["form"] enabled = curveData["overrideEnabled"] shape = newCurve.create(cvs, knots, degree, form, False, False, parent) mat = curveData.get("matrix") if parent == om2.MObject.kNullObj and shape.apiType( ) == om2.MFn.kTransform: parent = shape if enabled: plugs.setPlugValue(newCurve.findPlug("overrideEnabled", False), int(curveData["overrideEnabled"])) colours = curveData["overrideColorRGB"] nodes.setNodeColour(newCurve.object(), colours) if mat: cvData.append((newCurve.getPath(), mat, cvs)) # apparently must use object space to create and calling setCVPosition with the forloop causing an maya error # this could be because maya has yet to refresh meaning the MObject is invalid , hence the need to loop # back over and multiple the cvs by the worldMatrix for p, mat, cvs in cvData: mat = om2.MMatrix(mat) newCurve.setObject(p) for i in range(len(cvs)): cvs[i] *= mat newCurve.setCVPositions(cvs, om2.MSpace.kWorld) newCurve.updateCurve() return parent
def get_shape_data(curve): """Get all the necessary data to recreate the given curve.""" if not curve.hasFn(om2.MFn.kNurbsCurve): raise TypeError( "curve should have the `kNurbsCurve` function set, not {}". format(curve.apiTypeStr)) curve_fn = om2.MFnNurbsCurve(curve) name = curve_fn.name() knots = curve_fn.knots() cvs = curve_fn.cvPositions() form = curve_fn.form degree = curve_fn.degree overrideRGBColors = cmds.getAttr("{}.overrideRGBColors".format(name)) overrideColorRGB = cmds.getAttr("{}.overrideColorRGB".format(name))[0] overrideColorRGB = Color( overrideColorRGB[0], overrideColorRGB[1], overrideColorRGB[2], ) overrideEnabled = cmds.getAttr("{}.overrideEnabled".format(name)) useOutlinerColor = cmds.getAttr("{}.useOutlinerColor".format(name)) outlinerColor = cmds.getAttr("{}.outlinerColor".format(name)) outlinerColor = Color( outlinerColor[0], outlinerColor[1], outlinerColor[2], ) data = { "knots": knots, "cvs": cvs, "form": form, "degree": degree, "overrideRGBColors": overrideRGBColors, "overrideColorRGB": overrideColorRGB, "overrideEnabled": overrideEnabled, "useOutlinerColor": useOutlinerColor, "outlinerColor": outlinerColor, } return data
def get_shape_data(self): """ Gets the control nurbsCurves shapes data (points, degree, knot and periodic attributes) """ curve_shape = self.get_shape() if not isinstance(curve_shape, collections.Iterable): curve_shape = (curve_shape, ) self._shape = dict() for i, curve in enumerate(curve_shape): self._shape['shape{0}'.format(i)] = dict(degree=1, point=None, knot=None, periodic=True) curve_path = om.MGlobal.getSelectionListByName(curve).getDagPath(0) curve_fn = om.MFnNurbsCurve(curve_path) self._shape['shape{0}'.format(i)]['degree'] = curve_fn.degree self._shape['shape{0}'.format(i)]['point'] = component.get_cv_positions_from_curve(curve) self._shape['shape{0}'.format(i)]['knot'] = list(curve_fn.knots()) self._shape['shape{0}'.format(i)]['periodic'] = True if curve_fn.form == curve_fn.kPeriodic else False
def applyShapeData(ctrlName, crvData, transOffset=(0, 0, 0), rotOffset=(0, 0, 0), scaleOffset=(1, 1, 1)): """ Applies the supplied curve data to the specified control. [Args]: ctrlName (string) - The name of the control to change crvData (string) - The point data to apply transOffset (int, int, int) - The translation offset to apply to the new crv data rotOffset (int, int, int) - The rotation offset to apply to the new crv data scaleOffset (int, int, int) - The scale offset to apply to the new crv data """ curveShapes = utils.getShapeNodes(ctrlName) ctrlMObj = api.getMObj(ctrlName) if curveShapes: for each in curveShapes: cmds.delete(each) for crvShape in crvData.keys(): nurbsCrv = om.MFnNurbsCurve() cvArray = om.MPointArray() for i, x in enumerate(crvData[crvShape]['CVs']): cvArray.append((x['x'], x['y'], x['z'])) cvArray[i] = (api.transformMPoint(cvArray[i], rot=rotOffset, trans=transOffset, scale=scaleOffset)) knotArray = om.MDoubleArray() for x in crvData[crvShape]['knots']: knotArray.append(x) degree = crvData[crvShape]['degree'] form = crvData[crvShape]['form'] nurbsCrv.create(cvArray, knotArray, degree, form, 0, 1, ctrlMObj) if 'color' in crvData[crvShape].keys(): utils.setColor(nurbsCrv.name(), color=crvData[crvShape]['color']) shapes = utils.getShapeNodes(ctrlName) for i, each in enumerate(shapes): cmds.rename(each, '{}Shape{}'.format(ctrlName, i + 1))
def slice_curve_by_intervals(curve, intervals, error_threshold=0.01, begin_at_param=0, use_arc_length=False): sl = om.MSelectionList() sl.add(curve) curve_fn = om.MFnNurbsCurve(sl.getDagPath(0)) # use max_param/numSpans to obtain curve length for step size approximation # we use half of error_threshold to estimate step size max_param = curve_fn.numSpans curve_length = curve_fn.findLengthFromParam(max_param) u_step = max_param * (0.5 * error_threshold) / curve_length # we could use either arc length or linear distane func to compute the distance arc_len_func = lambda (s, t): curve_fn.findLengthFromParam( t) - curve_fn.findLengthFromParam(s) lin_dist_func = lambda (s, t): (curve_fn.getPointAtParam(t) - curve_fn. getPointAtParam(s)).length() dist_func = arc_len_func if use_arc_length else lin_dist_func # loop through intervals to find start and end points for each segment segments = [] u_ = begin_at_param for distance in intervals: u_0 = u_ u_ += u_step while u_ <= max_param: if abs(dist_func((u_0, u_)) - distance) < error_threshold: segments.append((curve_fn.getPointAtParam(u_0), curve_fn.getPointAtParam(u_))) break u_ += u_step return segments