Python源码示例:pymel.core.connectAttr()

示例1
def _duplicate_shape(old_shape):
    new_shape = pymel.createNode('nurbsCurve')

    # Transfert various attributes
    mel_dst = '{0}.create'.format(new_shape) # prevent annoying pymel warning
    pymel.connectAttr(old_shape.local, mel_dst)
    new_shape.create.evaluate() # Force maya to cache the shape before unconnecting
    pymel.disconnectAttr(old_shape.local, mel_dst)

    # Transfert various attributes
    for att_name in transferable_shape_attrs:
        att_old = old_shape.attr(att_name)
        att_new = new_shape.attr(att_name)
        att_new.set(att_old.get())

    return new_shape 
示例2
def source_nodes():
    cmds.file(new=True, force=True)

    source1, _ = pm.polyCube(name="source1")
    source2, _ = pm.polyCube(name="source2")
    target, _ = pm.polyCube(name="target")

    ch1 = att.addAttribute(source1,
                           "chanName",
                           "double",
                           0,
                           minValue=0,
                           maxValue=1)
    ch2 = att.addAttribute(source2,
                           "chanName",
                           "double",
                           0,
                           minValue=0,
                           maxValue=1)

    pm.connectAttr(ch1, source1.ty)
    pm.connectAttr(ch2, source2.ty) 
示例3
def gear_intmatrix_op(mA, mB, blend=0):
    """
    create mGear interpolate Matrix node.

    Arguments:
        mA (matrix): Input matrix A.
        mB (matrix): Input matrix A.
        blend (float or connection): Blending value.

    Returns:
        pyNode: Newly created mGear_intMatrix node
    """
    node = pm.createNode("mgear_intMatrix")

    pm.connectAttr(mA, node + ".matrixA")
    pm.connectAttr(mB, node + ".matrixB")

    if (isinstance(blend, str)
        or isinstance(blend, unicode)
            or isinstance(blend, pm.Attribute)):
        pm.connectAttr(blend, node + ".blend")
    else:
        pm.setAttr(node + ".blend", blend)

    return node 
示例4
def gear_curvecns_op(crv, inputs=[]):
    """
    create mGear curvecns node.

    Arguments:
        crv (nurbsCurve): Nurbs curve.
        inputs (List of dagNodes): Input object to drive the curve. Should be
            same number as crv points.
            Also the order should be the same as the points

    Returns:
        pyNode: The curvecns node.
    """
    pm.select(crv)
    node = pm.deformer(type="mgear_curveCns")[0]

    for i, item in enumerate(inputs):
        pm.connectAttr(item + ".worldMatrix", node + ".inputs[%s]" % i)

    return node 
示例5
def gear_inverseRotorder_op(out_obj, in_obj):
    """
    Apply a sn_inverseRotorder_op operator

    Arguments:
        out_obj (dagNode): Output object.
        in_obj (dagNode): Input object.

    Returns:
        pyNode: The newly created operator.
    """
    node = pm.createNode("mgear_inverseRotOrder")

    pm.connectAttr(in_obj + ".ro", node + ".ro")
    pm.connectAttr(node + ".output", out_obj + ".ro")

    return node 
示例6
def connectSet(source, target, testInstance):
    """Connect or set attributes

    Connects or set attributes depending if is instance of a instance check

    Args:
        source (str or Attr): Striname of the attribute or PyNode attribute
        target (str or Attr): Striname of the attribute or PyNode attribute
        testInstance (tuple): Tuple of types to check
    """
    if not isinstance(testInstance, tuple):
        testInstance = tuple(testInstance)

    if isinstance(source, testInstance):
        pm.connectAttr(source, target)
    else:
        pm.setAttr(target, source) 
示例7
def createDecomposeMatrixNode(m):
    """
    Create and connect a decomposeMatrix node.

    Arguments:
        m(str or attr): The matrix attribute name.

    Returns:
        pyNode: the newly created node.

    >>> dm_node = nod.createDecomposeMatrixNode(mulmat_node+".output")

    """
    node = pm.createNode("decomposeMatrix")

    pm.connectAttr(m, node + ".inputMatrix")

    return node 
示例8
def createCurveInfoNode(crv):
    """Create and connect a curveInfo node.

    Arguments:
        crv (dagNode): The curve.

    Returns:
        pyNode: the newly created node.

    >>> crv_node = nod.createCurveInfoNode(self.slv_crv)

    """
    node = pm.createNode("curveInfo")

    shape = pm.listRelatives(crv, shapes=True)[0]

    pm.connectAttr(shape + ".local", node + ".inputCurve")

    return node


# TODO: update using plusMinusAverage node 
示例9
def createVertexPositionNode(inShape,
                             vId=0,
                             output=None,
                             name="mgear_vertexPosition"):
    """Creates a mgear_vertexPosition node"""
    node = pm.createNode("mgear_vertexPosition", n=name)
    inShape.worldMesh.connect(node.inputShape)
    node.vertex.set(vId)
    if output:
        pm.connectAttr(output.parentInverseMatrix,
                       node.drivenParentInverseMatrix)
        pm.connectAttr(node.output, output.translate)

    return node


#############################################
# CREATE MULTI NODES
############################################# 
示例10
def controller_tag_connect(ctt, tagParent):
    """Summary

    Args:
        ctt (TYPE): Teh control tag
        tagParent (TYPE): The object with the parent control tag
    """
    if pm.controller(tagParent, q=True):
        tpTagNode = pm.PyNode(pm.controller(tagParent, q=True)[0])
        tpTagNode.cycleWalkSibling.set(True)
        pm.connectAttr(tpTagNode.prepopulate, ctt.prepopulate, f=True)

        ni = attribute.get_next_available_index(tpTagNode.children)
        pm.disconnectAttr(ctt.parent)
        pm.connectAttr(ctt.parent, tpTagNode.attr(
                       "children[%s]" % str(ni))) 
示例11
def _save_settings(self):
        """save settings inside objects pivotData attribute
        """
        # data to be save :
        # -----------------
        # futurePivot node

        # create attributes
        self._create_data_attribute()

        # connect futurePivot node
        pm.connectAttr(
            '%s%s' % (self._futurePivot.name(), ".message"),
            self._object.attr("pivotData.futurePivot"),
            f=True
        ) 
示例12
def make_stretchy(self):
        #check joints
        """


        """
        self._scaleMD = pm.createNode("multiplyDivide",
                                      n=self.limbName + "_scaleMD")
        pm.connectAttr(self.curve.curveInfo.arcLength, self.scaleMD.input1X)
        pm.setAttr(self.scaleMD.input2X, self.curve.arclen)
        pm.setAttr(self.scaleMD.operation, 2)

        for jnt in self.joints.jointChain:
            factor = pm.createNode("multiplyDivide", n="factor_" + jnt)
            pm.connectAttr(self.scaleMD.outputX, factor.input1X)
            pm.setAttr(factor.input2X, (pm.getAttr(jnt.ty)))
            pm.connectAttr(factor.outputX, jnt.ty) 
示例13
def _buildBaseCtrls(self):
        # create global ctrl
        self.globalCtrl = mayautils.createCtrl("{0}_all_ctrl".format(self.prefix), "crossArrow", 1, "yellow")
        globalCtrlAttr = [
            {"ln":"globalScale", "at":"float", "dv":1, "k":1},
            {"ln":self.RIG_TOP_TAG, "dt":"string"}
        ]
        mayautils.addAttributes(self.globalCtrl, globalCtrlAttr)

        # create meta ctrl
        self.metaCtrl = mayautils.createCtrl("{0}_meta_ctrl".format(self.prefix), "fatCross", 1, "yellow", None, [0,0,90])
        pm.xform(self.metaCtrl, t=self.metaPos, ws=1)
        mayautils.aimObject(self.endPos, self.metaCtrl)
        mayautils.createParentTransform("org", self.metaCtrl).setParent(self.globalCtrl)

        # build globalScale connections
        for ch in 'xyz':
            pm.connectAttr(self.globalCtrl.globalScale, "{0}.s{1}".format(self.metaCtrl.name(), ch))
            pm.setAttr("{0}.s{1}".format(self.metaCtrl.name(), ch), cb=0, keyable=0, lock=1)
            pm.setAttr("{0}.s{1}".format(self.globalCtrl.name(), ch), cb=0, keyable=0, lock=1) 
示例14
def on_action_axisChange(self, iRow, iCol, *args):
        pCell = self.ui.tblData.item(iRow,iCol)
        pData = pCell.data(QtCore.Qt.UserRole)

        pData.nChildLoc.axis.set(args[0])

        #Delete old connection
        pymel.disconnectAttr(pData.nMD.input1X)

        lstXCon = pData.nDecM.outputRotateX.listConnections()
        pymel.disconnectAttr(pData.nDecM.outputRotateX)
        pymel.delete(lstXCon)

        lstYCon = pData.nDecM.outputRotateY.listConnections()
        pymel.disconnectAttr(pData.nDecM.outputRotateY)
        pymel.delete(lstYCon)

        lstZCon = pData.nDecM.outputRotateZ.listConnections()
        pymel.disconnectAttr(pData.nDecM.outputRotateZ)
        pymel.delete(lstZCon)

        if args[0] == 0:
            pymel.connectAttr(pData.nDecM.outputRotateX, pData.nMD.input1X, f=True)
        elif args[0] == 1:
             pymel.connectAttr(pData.nDecM.outputRotateY, pData.nMD.input1X, f=True)
        elif args[0] == 2:
             pymel.connectAttr(pData.nDecM.outputRotateZ, pData.nMD.input1X, f=True)

    #Change the axis connection of the system to connect in the good axis 
示例15
def curvecns_op(crv, inputs=[]):

    for i, item in enumerate(inputs):
        node = pm.createNode("decomposeMatrix")
        pm.connectAttr(item + ".worldMatrix[0]", node + ".inputMatrix")
        pm.connectAttr(node + ".outputTranslate",
                       crv + ".controlPoints[%s]" % i)

    return node 
示例16
def oriCns(driver, driven, maintainOffset=False):
    """Apply orientation constraint

    Apply orientation constraint changing XYZ  default connexions by
    rotate compound connexions

    Note:
        We have found an evaluation difference in the values if the connexion
        is compound or by axis

    Arguments:
        driver (dagNode or dagNode list): Driver object.
        driven (dagNode): Driven object.
        maintainOffset (bool): Keep the offset.

    Returns:
        pyNode: Orientation constraintn node.

    Example:
        .. code-block:: python

            import mgear.core.applyop as aop
            import pymel.core as pm
            sphere = pm.polySphere(n='sphereDriver')
            cube = pm.polyCube(n='cubeDriven')
            ori_cns = aop.oriCns(sphere[0], cube[0], True)

    """
    oriCns = pm.orientConstraint(driver, driven, maintainOffset=maintainOffset)
    for axis in "XYZ":
        pm.disconnectAttr(oriCns + ".constraintRotate" + axis,
                          driven + ".rotate" + axis)
    pm.connectAttr(oriCns + ".constraintRotate", driven + ".rotate", f=True)

    return oriCns 
示例17
def pathCns(obj, curve, cnsType=False, u=0, tangent=False):
    """
    Apply a path constraint or curve constraint.

    Arguments:
        obj (dagNode): Constrained object.
        curve (Nurbscurve): Constraining Curve.
        cnsType (int): 0 for Path Constraint, 1 for Curve
            Constraint (Parametric).
        u (float): Position of the object on the curve (from 0 to 100 for path
            constraint, from 0 to 1 for Curve cns).
        tangent (bool): Keep tangent orientation option.

    Returns:
        pyNode: The newly created constraint.
    """
    node = pm.PyNode(pm.createNode("motionPath"))
    node.setAttr("uValue", u)
    node.setAttr("fractionMode", not cnsType)
    node.setAttr("follow", tangent)

    pm.connectAttr(curve.attr("worldSpace"), node.attr("geometryPath"))
    pm.connectAttr(node.attr("allCoordinates"), obj.attr("translate"))
    pm.connectAttr(node.attr("rotate"), obj.attr("rotate"))
    pm.connectAttr(node.attr("rotateOrder"), obj.attr("rotateOrder"))
    pm.connectAttr(node.attr("message"), obj.attr("specifiedManipLocation"))

    return node

# TODO: review function to make wupObject optional 
示例18
def gear_spring_op(in_obj, goal=False):
    """Apply mGear spring node.

    Arguments:
        in_obj (dagNode): Constrained object.
        goal (dagNode): By default is False.

    Returns:
        pyNode: Newly created node
    """
    if not goal:
        goal = in_obj

    node = pm.createNode("mgear_springNode")

    pm.connectAttr("time1.outTime", node + ".time")
    dm_node = pm.createNode("decomposeMatrix")
    pm.connectAttr(goal + ".parentMatrix", dm_node + ".inputMatrix")
    pm.connectAttr(dm_node + ".outputTranslate", node + ".goal")

    cm_node = pm.createNode("composeMatrix")
    pm.connectAttr(node + ".output", cm_node + ".inputTranslate")

    mm_node = pm.createNode("mgear_mulMatrix")

    pm.connectAttr(cm_node + ".outputMatrix", mm_node + ".matrixA")
    pm.connectAttr(in_obj + ".parentInverseMatrix", mm_node + ".matrixB")

    dm_node2 = pm.createNode("decomposeMatrix")
    pm.connectAttr(mm_node + ".output", dm_node2 + ".inputMatrix")
    pm.connectAttr(dm_node2 + ".outputTranslate", in_obj + ".translate")

    pm.setAttr(node + ".stiffness", 0.5)
    pm.setAttr(node + ".damping", 0.5)

    return node 
示例19
def gear_curveslide2_op(outcrv,
                        incrv,
                        position=0,
                        maxstretch=1,
                        maxsquash=1,
                        softness=0):
    """Apply a sn_curveslide2_op operator

    Arguments:
        outcrv (NurbsCurve): Out Curve.
        incrv (NurbsCurve):  In Curve.
        position (float): Default position value (from 0 to 1).
        maxstretch (float): Default maxstretch value (from 1 to infinite).
        maxsquash (float): Default maxsquash value (from 0 to 1).
        softness (float): Default softness value (from 0 to 1).

    Returns:
        pyNode: The newly created operator.
    """
    pm.select(outcrv)
    node = pm.deformer(type="mgear_slideCurve2")[0]

    pm.connectAttr(incrv + ".local", node + ".master_crv")
    pm.connectAttr(incrv + ".worldMatrix", node + ".master_mat")

    pm.setAttr(node + ".master_length", pm.arclen(incrv))
    pm.setAttr(node + ".slave_length", pm.arclen(incrv))
    pm.setAttr(node + ".position", 0)
    pm.setAttr(node + ".maxstretch", 1)
    pm.setAttr(node + ".maxsquash", 1)
    pm.setAttr(node + ".softness", 0)

    return node 
示例20
def gear_spinePointAtOp(cns, startobj, endobj, blend=.5, axis="-Z"):
    """
    Apply a SpinePointAt operator

    Arguments:
        cns (Constraint): The constraint to apply the operator on (must be a
            curve, path or direction constraint).
        startobj (dagNode): Start Reference.
        endobj (dagNode): End Reference.
        blend (float): Blend influence value from 0 to 1.
        axis (string): Axis direction.

    Returns:
        pyNode: The newly created operator.
    """
    node = pm.createNode("mgear_spinePointAt")

    # Inputs
    pm.setAttr(node + ".blend", blend)
    pm.setAttr(node + ".axe", ["X", "Y", "Z", "-X", "-Y", "-Z"].index(axis))

    pm.connectAttr(startobj + ".rotate", node + ".rotA")
    pm.connectAttr(endobj + ".rotate", node + ".rotB")

    # Outputs
    pm.setAttr(cns + ".worldUpType", 3)

    pm.connectAttr(node + ".pointAt", cns + ".worldUpVector")

    return node 
示例21
def gear_spinePointAtOpWM(cns, startobj, endobj, blend=.5, axis="-Z"):
    """
    Apply a SpinePointAt operator using world matrix

    Arguments:
        cns Constraint: The constraint to apply the operator on (must be a
            curve, path or direction constraint).
        startobj (dagNode): Start Reference.
        endobj (dagNode): End Reference.
        blend (float): Blend influence value from 0 to 1.
        axis (str): Axis direction.

    Returns:
        pyNode: The newly created operator.
    """
    node = pm.createNode("mgear_spinePointAt")

    # Inputs
    pm.setAttr(node + ".blend", blend)
    pm.setAttr(node + ".axe", ["X", "Y", "Z", "-X", "-Y", "-Z"].index(axis))

    dem_node1 = pm.createNode("decomposeMatrix")
    dem_node2 = pm.createNode("decomposeMatrix")
    pm.connectAttr(startobj + ".worldMatrix", dem_node1 + ".inputMatrix")
    pm.connectAttr(endobj + ".worldMatrix", dem_node2 + ".inputMatrix")

    pm.connectAttr(dem_node1 + ".outputRotate", node + ".rotA")
    pm.connectAttr(dem_node2 + ".outputRotate", node + ".rotB")

    # Outputs
    pm.setAttr(cns + ".worldUpType", 3)

    pm.connectAttr(node + ".pointAt", cns + ".worldUpVector")

    return node 
示例22
def reorderControllerChildrenTags(tag):
    """Clean the order on the children connection.

    This is important for the Left and right pick walk.
    Becasue is using the index of the connection.

    Arguments:
        tag (controller tag): The tag to clean the children order

    """
    ch = tag.children.connections()
    for i, c in enumerate(ch):
        d = c.children.connections(c.parent, p=True)[0]
        pm.disconnectAttr(c.parent, d)
        pm.connectAttr(c.parent, tag.attr("children[%s]" % str(i))) 
示例23
def get_next_available_index(attr):
    """get the next available index from a multi attr
    This function is a workaround because the connect attr flag next available
    is not working.

    The connectAttr to the children attribute is giving error
        i.e: pm.connectAttr(ctt.attr("parent"),
                             tpTagNode.attr("children"), na=True)
        if using the next available option flag
        I was expecting to use ctt.setParent(tagParent) but doest't work as
        expected.
        After reading the documentation this method looks prety
        useless.
        Looks like is boolean and works based on selection :(

    Args:
        attr (attr): Attr multi

    Returns:
        int: index
    """

    ne = attr.getNumElements()
    if ne == attr.numConnectedElements():
        return ne
    else:
        for e in range(ne):
            if not attr.attr(attr.elements()[e]).listConnections():
                return e

##########################################################
# Utility Channels
########################################################## 
示例24
def createMultMatrixNode(mA, mB, target=False, transform='srt'):
    """Create Maya multiply Matrix node.

    Note:
        This node have same functionality as the default Maya matrix
        multiplication.

    Arguments:
        mA (matrix): input matrix A.
        mB (matrix): input matrix B.
        target (dagNode): object target to apply the transformation
        transform (str): if target is True. out transform  to SRT valid
            value s r t

    Returns:
        pyNode: Newly created mGear_multMatrix node

    """
    node = pm.createNode("multMatrix")
    for m, mi in zip([mA, mB], ['matrixIn[0]', 'matrixIn[1]']):
        if isinstance(m, datatypes.Matrix):
            pm.setAttr(node.attr(mi), m)
        else:
            pm.connectAttr(m, node.attr(mi))
    if target:
        dm_node = pm.createNode("decomposeMatrix")
        pm.connectAttr(node + ".matrixSum",
                       dm_node + ".inputMatrix")
        if 't' in transform:
            pm.connectAttr(dm_node + ".outputTranslate",
                           target.attr("translate"), f=True)
        if 'r' in transform:
            pm.connectAttr(dm_node + ".outputRotate",
                           target.attr("rotate"), f=True)
        if 's' in transform:
            pm.connectAttr(dm_node + ".outputScale",
                           target.attr("scale"), f=True)

    return node 
示例25
def createDistNode(objA, objB, output=None):
    """Create and connect a distance node.

    Arguments:
        objA (dagNode): The dagNode A.
        objB (dagNode): The dagNode B.
        output (attr): Output attribute.

    Returns:
        pyNode: the newly created node.

    >>> distA_node = nod.createDistNode(self.tws0_loc, self.tws1_loc)

    """
    node = pm.createNode("distanceBetween")

    dm_nodeA = pm.createNode("decomposeMatrix")
    dm_nodeB = pm.createNode("decomposeMatrix")

    pm.connectAttr(objA + ".worldMatrix", dm_nodeA + ".inputMatrix")
    pm.connectAttr(objB + ".worldMatrix", dm_nodeB + ".inputMatrix")

    pm.connectAttr(dm_nodeA + ".outputTranslate", node + ".point1")
    pm.connectAttr(dm_nodeB + ".outputTranslate", node + ".point2")

    if output:
        pm.connectAttr(node + ".distance", output)

    return node 
示例26
def createAddNode(inputA, inputB):
    """Create and connect a addition node.

    Arguments:
        inputA (attr or float): The attribute input A
        inputB (attr or float): The attribute input B

    Returns:
        pyNode: the newly created node.

    >>> add_node = nod.createAddNode(self.roundness_att, .001)

    """
    node = pm.createNode("addDoubleLinear")

    if (isinstance(inputA, str)
            or isinstance(inputA, unicode)
            or isinstance(inputA, pm.Attribute)):
        pm.connectAttr(inputA, node + ".input1")
    else:
        pm.setAttr(node + ".input1", inputA)

    if (isinstance(inputB, str)
            or isinstance(inputB, unicode)
            or isinstance(inputB, pm.Attribute)):
        pm.connectAttr(inputB, node + ".input2")
    else:
        pm.setAttr(node + ".input2", inputB)

    return node


# TODO: update using plusMinusAverage node 
示例27
def createSubNode(inputA, inputB):
    """Create and connect a subtraction node.

    Arguments:
        inputA (attr or float): The attribute input A
        inputB (attr or float): The attribute input B

    Returns:
        pyNode: the newly created node.

    >>> sub_nod = nod.createSubNode(self.roll_att, angle_outputs[i-1])

    """
    node = pm.createNode("addDoubleLinear")

    if (isinstance(inputA, str)
            or isinstance(inputA, unicode)
            or isinstance(inputA, pm.Attribute)):
        pm.connectAttr(inputA, node + ".input1")
    else:
        pm.setAttr(node + ".input1", inputA)

    if (isinstance(inputB, str)
            or isinstance(inputB, unicode)
            or isinstance(inputB, pm.Attribute)):
        neg_node = pm.createNode("multiplyDivide")
        pm.connectAttr(inputB, neg_node + ".input1X")
        pm.setAttr(neg_node + ".input2X", -1)
        pm.connectAttr(neg_node + ".outputX", node + ".input2")
    else:
        pm.setAttr(node + ".input2", -inputB)

    return node 
示例28
def createDivNode(inputA, inputB, output=None):
    """Create and connect a Divide node.

    Arguments:
        inputA (attr, float or list of float): The attribute input A
        inputB (attr, float or list of float): The attribute input B
        output (attr or list of attr): The attribute to connect the
            output.

    Returns:
        pyNode: the newly created node.

    Example:
        .. code-block:: python

            # Classic Maya style creation and connection = 4 lines
            div1_node = pm.createNode("multiplyDivide")
            div1_node.setAttr("operation", 2)
            div1_node.setAttr("input1X", 1)
            pm.connectAttr(self.rig.global_ctl+".sx",
                           div1_node+".input2X")

            # mGear style = 1 line
            div1_node = nod.createDivNode(1.0,
                                          self.rig.global_ctl+".sx")

    """
    return createMulDivNode(inputA, inputB, 2, output) 
示例29
def createPlusMinusAverage1D(input, operation=1, output=None):
    """Create a multiple average node 1D.
    Arguments:
        input (attr, float or list): The input values.
        operation (int): Node operation. 0=None, 1=sum, 2=subtract,
            3=average
        output (attr): The attribute to connect the result.

    Returns:
        pyNode: the newly created node.

    """
    if not isinstance(input, list):
        input = [input]

    node = pm.createNode("plusMinusAverage")
    node.attr("operation").set(operation)

    for i, x in enumerate(input):
        try:
            pm.connectAttr(x, node + ".input1D[%s]" % str(i))
        except RuntimeError:
            pm.setAttr(node + ".input1D[%s]" % str(i), x)

    if output:
        pm.connectAttr(node + ".output1D", output)

    return node 
示例30
def createNegateNodeMulti(name, inputs=[]):
    """Create and connect multiple negate nodes

    Arguments:
        name (str): The name for the new node.
        inputs (list of attr): The list of attributes to negate

    Returns:
        list: The output attributes list.

    """
    s = "XYZ"
    count = 0
    i = 0
    outputs = []
    for input in inputs:
        if count == 0:
            real_name = name + "_" + str(i)
            node_name = pm.createNode("multiplyDivide", n=real_name)
            i += 1

        pm.connectAttr(input, node_name + ".input1" + s[count], f=True)
        pm.setAttr(node_name + ".input2" + s[count], -1)

        outputs.append(node_name + ".output" + s[count])
        count = (count + 1) % 3

    return outputs