沿曲线复制 nurbs 曲线以执行放样

Duplicate a nurbs curve along a curve to perform a loft

我编写了一个脚本来创建以下内容;开始和结束曲线形状和之间的线性曲线。 现在我想要做的是沿着路径复制和转换起始曲线形状(如图所示),然后执行放样(首选,因为可能会给出最干净的结果),或者在两者之间放样现有曲线形状,然后将放样几何体变形为曲线。后者我试过了;

pm.deformer((loftShape, path), type='curveWarp', name='curveWarp#')

没有成功。定位器是计算生成正确的点 给定不同距离/起始角度的贝塞尔曲线。我本以为辛苦的工作已经完成了,但我在这看似简单的最后一步上遇到了麻烦。

下面是我整理的查询曲线信息的方法:

def getClosestCV(x, curves, tolerance=0.0):
    '''Find the closest control vertex between the given vertices, CVs, or objects and each of the given curves.

    :Parameters:
        x (str)(obj)(list) = Polygon vertices, control vertices, objects, or points given as (x,y,z) tuples.
        curves (str)(obj)(list) = The reference object in which to find the closest CV for each vertex in the list of given vertices.
        tolerance (int)(float) = Maximum search distance. Default is 0.0, which turns off the tolerance flag.

    :Return:
        (dict) closest vertex/cv pairs (one pair for each given curve) ex. {<vertex from set1>:<vertex from set2>}.

    ex. vertices = Init.getComponents(objects, 'vertices')
        closestVerts = getClosestCV(curve0, curves)
    '''
    pm.undoInfo(openChunk=True)
    x = pm.ls(x, flatten=1) #assure x arg is a list (if given as str or single object).

    npcNode = pm.ls(pm.createNode('nearestPointOnCurve'))[0] #create a nearestPointOnCurve node.

    result={}
    for curve in pm.ls(curves):

        pm.connectAttr(curve.worldSpace, npcNode.inputCurve, force=1) #Connect the curve's worldSpace geometry to the npc node.

        for i in x:
            if not isinstance(i, (tuple, list, set)):
                pos = pm.pointPosition(i)
            else:
                pos = i
            pm.setAttr(npcNode.inPosition, pos)

            distance = Init.getDistanceBetweenTwoPoints(pos, pm.getAttr(npcNode.position))
            p = pm.getAttr(npcNode.parameter)
            if not tolerance:
                result[i] = p
            elif distance < tolerance:
                result[i] = p

    pm.delete(npcNode)

    pm.undoInfo(closeChunk=True)

    return result

def getCvInfo(c, returnType='cv', filter_=[]):
    '''Get a dict containing CV's of the given curve(s) and their corresponding point positions (based on Maya's pointOnCurve command).

    :Parameters:
        - c (str)(obj)(list) = Curves or CVs to get CV info from.
        - returnType (str) = The desired returned values. Default is 'cv'.
            valid values are: 
                'cv' = Return a list of all CV's for the given curves.
                'count' = Return an integer representing the total number of cvs for each of the curves given.
                'parameter', 'position', 'index', 'localPosition', 'tangent', 'normalizedTangent', 'normal', 'normalizedNormal', 'curvatureRadius', 'curvatureCenter'
                = Return a dict with CV's as keys and the returnType as their corresponding values.
            ex. {NurbsCurveCV(u'polyToCurveShape7.cv[5]'): [-12.186520865542082, 15.260936896515751, -369.6159740743584]}
        - filter_ (str)(obj)(list) = Value(s) to filter for in the returned results.

    :Return:
        (dict)(list)(int) dependant on returnType.

    ex. cv_tan = getCvInfo(curve.cv[0:2],'tangent') #get CV tangents for cvs 0-2.
    ex. cvParam = getCvInfo(curve, 'parameters') #get the curves CVs and their corresponding U parameter values.
    ex. filtered = getCvInfo(<curve>, 'normal', <normal>) #filter results for those that match the given value.
    '''
    result={}
    for curve in pm.ls(c):

        if '.cv' in str(curve): #if CV given.
            cvs = curve
            curve = pm.listRelatives(cvs, parent=1)
        else: #if curve(s) given
            cvs = curve.cv

        parameters = Init.getClosestCV(cvs, curve) #use getClosestCV to get the parameter location for each of the curves CVs.
        for cv, p in parameters.items():

            if returnType is 'position': # Get cv position
                v = pm.pointOnCurve(curve, parameter=p, position=True)
            elif returnType is 'localPosition':
                v = pm.getAttr(cv) # local cv position
            elif returnType is 'tangent': # Get cv tangent
                v = pm.pointOnCurve(curve, parameter=p, tangent=True)
            elif returnType is 'normalizedTangent':
                v = pm.pointOnCurve(curve, parameter=p, normalizedTangent=True)
            elif returnType is 'normal': # Get cv normal
                v = pm.pointOnCurve(curve, parameter=p, normal=True)
            elif returnType is 'normalizedNormal':
                v = pm.pointOnCurve(curve, parameter=p, normalizedNormal=True) #Returns the (x,y,z) normalized normal of curve1 at parameter 0.5.
            elif returnType is 'curvatureRadius': # Get cv curvature
                v = pm.pointOnCurve(curve, parameter=p, curvatureRadius=True) #Returns the curvature radius of curve1 at parameter 0.5.
            elif returnType is 'curvatureCenter':
                v = pm.pointOnCurve(curve, parameter=p, curvatureCenter=True)
            elif returnType is 'parameter': # Return the CVs parameter.
                v = p
            elif returnType is 'count': # total number of cv's for the curve.
                result[curve] = len(Init.getCvInfo(curve))
                break
            elif returnType is 'index': # index of the cv
                s = str(cv)
                v = int(s[s.index('[')+1:s.index(']')])
            else:
                v = None

            result[cv] = v

    if returnType is 'cv':
        result = result.keys()

    if filter_:
        if not isinstance(filter_, (tuple, set, list)):
            filter_ = list(filter_)
        try:
            result = {k:v for k,v in result.items() if any((v in filter_, v==filter_))}
        except AttributeError:
            result = [i for i in result if any((i in filter_, i==filter_))]

    if len(result) is 1:
        try:
            result = result.values()[0]
        except AttributeError, TypeError:
            result = result[0]

    return result

我最终决定为此使用内置的 MASH 插件。也许这对以后的人有帮助。

def duplicateAlongCurve(path, start, count=6, geometry='Instancer'):
    '''Duplicate objects along a given curve using MASH.

    :Parameters:
        path (obj) = The curve to use as a path.
        start () = Starting object.
        count (int) = The number of duplicated objects. (point count on the MASH network)
        geometry (str) = Particle instancer or mesh instancer (Repro node). (valid: 'Mesh' (default), 'Instancer')

    :Return:
        (list) The duplicated objects in order of start to end.
    '''
    pm.undoInfo(openChunk=1)

    #create a MASH network
    import MASH.api as mapi
    mashNW = mapi.Network()
    mashNW.MTcreateNetwork(start, geometry=geometry, hideOnCreate=False) #MASH_tools module (derived from 'createNetwork')

    curveNode = pm.ls(mashNW.addNode('MASH_Curve').name)[0]
    pm.connectAttr(path.worldSpace[0], curveNode.inCurves[0], force=1)

    pm.setAttr(curveNode.stopAtEnd, 1) #0=off, 1=on
    pm.setAttr(curveNode.clipStart, 0)
    pm.setAttr(curveNode.clipEnd, 1)
    pm.setAttr(curveNode.timeStep, 1)
    pm.setAttr(curveNode.curveLengthAffectsSpeed, 1)

    distNode = pm.ls(mashNW.distribute)[0]
    pm.setAttr(distNode.pointCount, count)
    pm.setAttr(distNode.amplitudeX, 0)

    instNode = pm.ls(mashNW.instancer)[0]
    baked_curves = mashNW.MTbakeInstancer(instNode) #MASH_tools module (derived from 'MASHbakeInstancer')

    result=[start]
    for curve in reversed(baked_curves):
        result.append(curve)

    pm.delete(mashNW.waiter.name()) #delete the MASH network.
    pm.undoInfo(closeChunk=1)

    return result