예제 #1
0
def createWorld(name='human', scale=1):
    '''
	Create World node for character with standardized Rigg Groups and export Sets
	Creates Placer, Mover, Direction Ctrls
	Args:
	    name: Name of world
	Returns:

	'''
    if pm.objExists('%s_world' % name):
        lg.info('createWorld: Deleting existing %s_world' % name)
        pm.delete('%s_world' % name)
    world = pm.createNode('transform', n='%s_world' % name)
    pm.createNode('transform', n='CONTROLS'), pm.createNode(
        'transform', n='RIG'), pm.createNode('transform', n='GEO')
    pm.parent(['CONTROLS', 'RIG', 'GEO'], world)
    pm.createNode('transform', n='CONTSTRAINER')
    pm.parent('CONTSTRAINER', 'CONTROLS')
    pm.createNode('transform',
                  n='render_Geo'), pm.createNode('transform',
                                                 n='nonRender_Geo')
    pm.parent(['render_Geo', 'nonRender_Geo'], 'GEO')
    pm.createNode('transform', n='RIG_NONSCALE')
    pm.parent('RIG_NONSCALE', 'RIG')
    rigscale = pm.createNode('transform', n='RIG_SCALE')
    pm.parent('RIG_SCALE', 'RIG')

    print 'placer'
    placer = curveLib.createShapeCtrl(type='PLACER',
                                      name='PLACER',
                                      scale=scale)
    print 'mover'
    mover = curveLib.createShapeCtrl(type='MOVER',
                                     name='MOVER',
                                     scale=scale,
                                     color='blue')
    print 'direction'
    direction = curveLib.createShapeCtrl(type='DIRECTION',
                                         name='DIRECTION',
                                         scale=scale,
                                         color='red')

    # size attribute
    pm.addAttr(direction, ln='size', at='double', dv=1, k=1)
    direction.size >> direction.scaleX
    direction.size >> direction.scaleY
    direction.size >> direction.scaleZ
    direction.size >> rigscale.scaleX
    direction.size >> rigscale.scaleY
    direction.size >> rigscale.scaleZ

    pm.parent(direction, mover)
    pm.parent(mover, placer)
    pm.parent(placer, 'CONTSTRAINER')

    # DISPLAY
    curveLib.createTextCtrl('D', 'DISPLAY', font="Arial", size=scale)
    riggUtils.grpCtrl(ctrl='DISPLAY')
    pm.parent('DISPLAY_ZERO', placer)
    riggUtils.makeExportable([placer, mover, direction, 'DISPLAY'])
예제 #2
0
def build_ctrlJnts_leg(jnts={}, side='L', hook='C_pelvis01_ctrlJnt'):
    '''
    create ctrl joints for leg
    '''
    log_debug('side is %s' % side)
    pm.delete(pm.ls('%s_leg_jntGrp' % side))
    pm.createNode('transform', name='%s_leg_jntGrp' % side)
    riggUtils.grpIn('Skeleton_grp', '%s_leg_jntGrp' % side)

    # leg
    leg = pm.duplicate('%s_leg01_skinJnt' % side,
                       n='%s_leg01_ctrlJnt' % side)[0]
    jnts['%s_leg01' % side] = {'ctrlJnt': leg}
    pm.select(leg)
    pm.mel.searchReplaceNames("_skinJnt", "_ctrlJnt", "hierarchy")
    pm.parent(leg, '%s_leg_jntGrp' % side)
    riggUtils.grpCtrl(leg)

    log_debug('constraining to hook %s' % leg.ZERO.get())

    if hook != '':
        pm.parentConstraint(hook, leg.AUTO.get(), mo=1)

    # deal with children
    for childJnt in pm.listRelatives(leg, children=1, type='joint', ad=1):
        log_debug('childJnt leg %s' % childJnt)
        if childJnt == ('%s_foot01_ctrlJnt' % side):
            pm.rename(childJnt, '%s_leg03_ctrlJntEnd' %
                      side)  # create end joint from foot
            # delete fingers
            pm.delete(
                pm.listRelatives('%s_leg03_ctrlJntEnd' % side,
                                 children=1,
                                 ad=1))
            break

    # foot
    foot = pm.duplicate('%s_foot01_skinJnt' % side,
                        n='%s_foot01_ctrlJnt' % side)[0]
    jnts['%s_foot' % side] = {'ctrlJnt': foot}

    pm.select(foot)
    pm.mel.searchReplaceNames("_skinJnt", "_ctrlJnt", "hierarchy")

    riggUtils.grpIn('%s_leg_jntGrp' % side, foot)
    riggUtils.grpCtrl(foot)

    # constrain foot AUTO to leg03End
    pm.pointConstraint('%s_leg03_ctrlJntEnd' % side,
                       pm.getAttr('%s_foot01_ctrlJnt.AUTO' % side))
예제 #3
0
def create_ctrl_ikfkSwitch(name='L_leg_fkIkSwitch',
                           color='blue',
                           parent=None,
                           scale=1):
    """
    Args:
        name:
        color:
        parent:
    Returns:

    """
    fkikSwitch = curveLib.createShapeCtrl(type='crossPaddle',
                                          name=name,
                                          scale=scale,
                                          color=color)
    # addd 'rotOrder'

    lmin = -90
    lmax = 90

    # fkik stretchy
    attrs = ['fkIk', 'stretchy']
    pm.addAttr(fkikSwitch, ln='fkIk', at='double', k=1, min=0, max=1)
    pm.addAttr(fkikSwitch, ln='stretchy', at='long', k=1, min=0, max=1)

    # group and limit
    zero = riggUtils.grpCtrl(fkikSwitch)
    riggUtils.cleanUpAttr(
        sel=fkikSwitch,
        listAttr=['sx', 'sy', 'sz', 'rx', 'ry', 'rz', 'tx', 'ty', 'tz'],
        l=0,
        k=0,
        cb=0)
    if parent is not None:
        riggUtils.snap(parent, zero)
        pm.parent(zero, parent)

    return fkikSwitch
예제 #4
0
def create_ctrl_spine(jnts, spine_count=4, scale=1):
    '''
    Create COG, pelvis, spine and chest ctrls

    Args:
        jnts:
        spine_count:
        size: ctrl scale

    Returns:

    '''
    ### TODO cest offset control, to rotate from chest if needed (human_luma:C_chest_offsetCtrl)
    log_info('Creating Spine Ctrls')
    side = 'C'
    scale = scale * 2 # scale multiplyier

    # - COG - #

    # create ctrl
    cog = curveLib.wireController(type='circle', name='C_COG_ctrl', size=scale*1.25, color='yellow', facingAxis='y+')
    riggUtils.cleanUpAttr(sel=[cog], listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
    # grp - snap - parent - gimbal
    riggUtils.grpCtrl(cog)
    riggUtils.snap(jnts['pelvis01']['ctrlJnt'], cog.ZERO.get(), 'point')
    pm.parent(cog.ZERO.get(), 'DIRECTION')
    riggUtils.addGimbal(cog)
    # add to dictionary
    jnts['cog'] = {'ctrl': pm.PyNode('C_COG_ctrl')}

    spine_ctrl_grp = pm.createNode('transform', n='C_spineCtrl_grp', p=cog.gimbal.get())
    riggUtils.snap(cog.gimbal.get(), spine_ctrl_grp)
    
    
    # - Pelvis - #

    log_debug('creating pelvis ctrls')
    jnts['pelvis01']['ctrl'] = curveLib.wireController(type='circle', name='C_pelvis_ctrl', size=scale,
                                                       color='red', facingAxis='y+', aOffset=[0, -0.5, 0])
    riggUtils.cleanUpAttr(sel=jnts['pelvis01']['ctrl'], listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
    # rotation order
    jnts['pelvis01']['ctrlJnt'].rotateOrder.set(2)
    jnts['pelvis01']['ctrl'].rotateOrder.set(2)
    # grp - snap - parent - gimbal
    riggUtils.grpCtrl(jnts['pelvis01']['ctrl'])
    riggUtils.snap(jnts['pelvis01']['ctrlJnt'], jnts['pelvis01']['ctrl'].ZERO.get(), 'point')
    pm.parent(jnts['pelvis01']['ctrl'].ZERO.get(), spine_ctrl_grp)
    riggUtils.addGimbal(jnts['pelvis01']['ctrl'])

    # - Spine - #

    # ik spine
    
    log_debug('creating ik spine ctrls %s' % spine_count)
    spine_ik_jnts = pm.ls('C_spine*_ikJnt*')

    if len(spine_ik_jnts) < 3:
        log_info("No spine*_fkJnts found. Skipping")   
    else:
        parent_to=spine_ctrl_grp 
    
        # for ik ctrl, we skip 01 and 03, we will parent those to pelvis and chest, we only want a 'C_spine_midCtrl'
        ctrl = curveLib.wireController(type='cube', name='C_spineMid_ikCtrl', size=scale *0.25,
                                                        color='blue', facingAxis='y+',  aOffset=[0, 0, -3])
        riggUtils.cleanUpAttr(ctrl, listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
        riggUtils.grpCtrl(ctrl)
        riggUtils.snap(spine_ik_jnts[1], ctrl.ZERO.get(), 'point')
        pm.parent(ctrl.ZERO.get(), parent_to)
        
        # add to dic    
        jnts['spine02']['ikCtrl'] = ctrl

        #parent jnt
        pm.parent(spine_ik_jnts[1], ctrl)
    
    
    # fk spine

    log_debug('creating fk spine ctrls %s' % spine_count)
    spine_fk_jnts = pm.ls('C_spine*_ctrlJnt')

    if len(spine_fk_jnts) == 0:
        log_info("No spine*_fkJnts found. Skipping")
    
    parent_to=spine_ctrl_grp

    for i in range(0, len(spine_fk_jnts)):
        jnt_nr = '%02d' % ( i + 1)
        ctrl = curveLib.wireController(type='circle', name='C_spine%s_fkCtrl'%jnt_nr, size=scale,
                                                      color='blue', facingAxis='y+')
        riggUtils.cleanUpAttr(ctrl, listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
        riggUtils.grpCtrl(ctrl)
        riggUtils.snap(spine_fk_jnts[i], ctrl.ZERO.get(), 'point')
        pm.parent(ctrl.ZERO.get(), parent_to)
        
        # parent next jnt to this ctrl
        parent_to = ctrl

        # add to dic
        jnts['spine%s'%jnt_nr]['fkCtrl'] = ctrl

    # - Chest - #

    log_debug('creating chest ctrls')
    jnts['chest01']['ctrl'] = curveLib.wireController(type='circle', name='C_spineChest_ctrl', size=scale,
                                                      color='red', facingAxis='y+')
    riggUtils.cleanUpAttr(sel=jnts['chest01']['ctrl'], listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
    # rotation order
    jnts['chest01']['ctrlJnt'].rotateOrder.set(2)
    jnts['chest01']['ctrl'].rotateOrder.set(2)

    # grp - snap - parent - gimbal
    riggUtils.grpCtrl(jnts['chest01']['ctrl'])
    riggUtils.snap(jnts['chest01']['ctrlJnt'], jnts['chest01']['ctrl'].ZERO.get(), 'point')
    pm.parent(jnts['chest01']['ctrl'].ZERO.get(), spine_ctrl_grp)
    riggUtils.addGimbal(jnts['chest01']['ctrl'])

    log_debug('spine_ik is %s'%spine_ik_jnts)
    
    # lets parent 01_ikJnt to pelvis ctrl and 03_ikJnt to chest (ik 2 has its own ctrl)
    if len(spine_ik_jnts) > 2:
        pm.parent(spine_ik_jnts[0],  jnts['pelvis01']['ctrl'])
        pm.parent(spine_ik_jnts[2],  jnts['chest01']['ctrl'])

        # align spineMid halfway between pelvis and chest
        pm.parentConstraint([jnts['pelvis01']['ctrl'], jnts['chest01']['ctrl']], jnts['spine02']['ikCtrl'].AUTO.get(), mo=1)
    
    log_debug('done create_ctrl_spine')
예제 #5
0
def create_ctrl_ik_leg(jnts, side='L', parent='DIRECTION', scale=1):
    '''
    Args:
        jnts: joint dictionary
        side: 'R' or 'L'
    Returns:   joint dictionary
    '''

    color = biped.define_color(side)
    id = '%s_foot01' % side
    # check if exists
    if pm.objExists('%s_ikCtrl' % id):
        pm.select('%s_ikCtrl' % id)
        return '%s_ikCtrl Exists' % id

    # -- ik ctrl -- #
    jnts[id]['ikCtrl'] = curveLib.createShapeCtrl(type='cube',
                                                  name='%s_ikCtrl' % id,
                                                  scale=scale,
                                                  color=color)
    # group and align
    zero = riggUtils.grpCtrl(jnts[id]['ikCtrl'])
    riggUtils.snap(jnts[id]['ctrlJnt'], zero, typeCnx='point')
    riggUtils.grpIn('%s_leg_ctrlGrp' % side, zero)  # parent leg_ctrlGrp

    # limit
    riggUtils.cleanUpAttr(sel=[jnts[id]['ikCtrl']],
                          listAttr=['sx', 'sy', 'sz'],
                          l=0,
                          k=0,
                          cb=0)
    log_debug('limit')

    # gimbal
    gimbalCtrl = pm.duplicate(jnts[id]['ikCtrl'], n='%s_ikGimbalCtrl' % id)[0]

    pm.parent(gimbalCtrl, jnts[id]['ikCtrl'])
    pm.xform('%s.cv[0:]' % gimbalCtrl.getShape(), s=(.8, .8, .8), r=1)
    pm.addAttr(jnts[id]['ikCtrl'], ln='gimbal_vis', at='bool', k=1)
    jnts[id]['ikCtrl'].gimbal_vis >> gimbalCtrl.visibility
    # store attr of gimbal on main ctrl
    pm.addAttr(jnts[id]['ikCtrl'], ln='gimbal', dt='string')
    jnts[id]['ikCtrl'].attr('gimbal').set('%s' % gimbalCtrl,
                                          k=0,
                                          l=0,
                                          type='string')
    log_debug('gimal done')

    # -- pole vector -- #
    jnts[id]['pvecCtrl'] = curveLib.createShapeCtrl(type='sphere',
                                                    name='%s_pvecCtrl' % id,
                                                    scale=scale,
                                                    color=color)
    # group and align
    zero = riggUtils.grpCtrl(jnts[id]['pvecCtrl'])
    log_debug('finding pole position')
    polePos = riggUtils.poleVectorPosition(
        startJnt=jnts['%s_leg01' % side]['ikJnt'],
        midJnt=jnts['%s_leg02' % side]['ikJnt'],
        endJnt=jnts['%s_foot01' % side]['ctrlJnt'],
        length=12,
        createLoc=0,
        createViz=0)
    log_debug('polepos is %s' % polePos)
    pm.xform(zero, ws=1, t=(polePos.x, polePos.y, polePos.z))
    riggUtils.grpIn('%s_leg_ctrlGrp' % side, zero)  # parent leg_ctrlGrp
    riggUtils.grpIn(parent, '%s_leg_ctrlGrp' % side)  # parent to DIRECTION

    # limit
    riggUtils.cleanUpAttr(sel=[jnts[id]['pvecCtrl']],
                          listAttr=['sx', 'sy', 'sz', 'rx', 'ry', 'rz'],
                          l=0,
                          k=0,
                          cb=0)

    log_info("Done create_ctrl_ik_leg()")

    return jnts
예제 #6
0
def create_ctrl_fk_leg(jnts, side='L', parent='DIRECTION', scale=1):
    '''
    Args:
        jnts: joint dictionary
        side: 'R' or 'L'
    Returns:   joint dictionary
    '''
    if side == 'L':
        color = 'blue'
    else:
        color = 'red'

    zero = ''
    ids = ['%s_leg01' % side, '%s_leg02' % side, '%s_leg03' % side]
    for id in ids:
        jnts[id]['fkCtrl'] = curveLib.wireController(type='circle',
                                                     name='%s_fkCtrl' % id,
                                                     size=(scale),
                                                     color=color,
                                                     facingAxis='x+')
        # pm.xform('%sShape.cv[0:]' % jnts[id]['fkCtrl'], s=(1.75, 1, 1), t=(1.5, 0, 0), r=1)
        # group and align
        zero = riggUtils.grpCtrl(ctrl=jnts[id]['fkCtrl'], sdk=1)
        riggUtils.snap(jnts[id]['fkJnt'], zero)
        riggUtils.grpIn('%s_leg_ctrlGrp' % side, zero)  # parent leg_ctrlGrp
        riggUtils.grpIn(parent, '%s_leg_ctrlGrp' % side)
        # limit
        riggUtils.cleanUpAttr(sel=[jnts[id]['fkCtrl']],
                              listAttr=['sx', 'sy', 'sz', 'tx', 'ty', 'tz'],
                              l=1,
                              k=0,
                              cb=0)

    # hide zero of leg03
    zero.hide()

    id = '%s_foot01' % side
    jnts[id]['fkCtrl'] = curveLib.wireController(type='circle',
                                                 name='%s_fkCtrl' % id,
                                                 size=(scale),
                                                 color=color,
                                                 facingAxis='x+')
    # group and align
    zero = riggUtils.grpCtrl(ctrl=jnts[id]['fkCtrl'], sdk=1)
    riggUtils.snap(jnts[id]['ctrlJnt'], zero)
    riggUtils.grpIn(
        '%s_leg_ctrlGrp' % side,
        zero)  # parent leg ctrlGrp, AUTO will be constraint to leg03_endJnt
    # limit
    riggUtils.cleanUpAttr(sel=[jnts[id]['fkCtrl']],
                          listAttr=['sx', 'sy', 'sz', 'tx', 'ty', 'tz'],
                          l=0,
                          k=0,
                          cb=0)

    id = '%s_foot02' % side
    jnts[id]['fkCtrl'] = curveLib.wireController(type='circle',
                                                 name='%s_fkCtrl' % id,
                                                 size=(scale),
                                                 color=color,
                                                 facingAxis='x+')
    # group and align
    zero = riggUtils.grpCtrl(ctrl=jnts[id]['fkCtrl'], sdk=1)
    riggUtils.snap(jnts[id]['ctrlJnt'], zero)
    riggUtils.grpIn(
        '%s_leg_ctrlGrp' % side,
        zero)  # parent leg ctrlGrp, AUTO will be constraint to leg03_endJnt
    riggUtils.grpIn(parent, '%s_leg_ctrlGrp' % side)  # parent to DIRECTION
    pm.parent(zero, jnts['%s_foot01' % side]['fkCtrl'])  # parent foot2
    # limit
    riggUtils.cleanUpAttr(sel=[jnts[id]['fkCtrl']],
                          listAttr=['sx', 'sy', 'sz', 'tx', 'ty', 'tz'],
                          l=0,
                          k=0,
                          cb=0)

    log_info("Done create_ctrl_fk_leg()")
예제 #7
0
def build_ctrlJnts_body(jntsDic={}):
    # delete existing
    pm.delete(pm.ls(['Skeleton_grp', 'C_body_jntGrp']))

    pm.createNode('transform', name='Skeleton_grp')
    pm.createNode('transform', name='C_body_jntGrp', p='Skeleton_grp')

    for bodyLimb in ['C_head', 'C_neck', 'C_spine']:
        pm.createNode('transform',
                      name='%s_jntGrp' % bodyLimb,
                      p='C_body_jntGrp')

    # spine
    ########################
    jntsDic['spine01'] = {
        'ctrlJnt': pm.duplicate('C_spine01_skinJnt', n='C_spine01_ctrlJnt')[0]
    }
    pm.select(jntsDic['spine01']['ctrlJnt'])
    pm.mel.searchReplaceNames("_skinJnt", "_ctrlJnt", "hierarchy")

    pm.parent(jntsDic['spine01']['ctrlJnt'], 'C_spine_jntGrp')
    riggUtils.grpCtrl(jntsDic['spine01']['ctrlJnt'])

    # number of spine jntsDic for naming endjoint
    spine_jnt_amount = len(pm.ls('C_spine*_ctrlJnt'))

    # deal with children, make endJnt or delete
    i = 2
    for childJnt in pm.listRelatives(jntsDic['spine01']['ctrlJnt'],
                                     children=1,
                                     type='joint',
                                     ad=1):
        if 'spine' in childJnt.name():  # spine02, 03 ...
            continue
        elif childJnt == 'C_chest01_ctrlJnt':  # end joint
            log_debug('endjoint. %s' % childJnt)
            pm.rename(childJnt, 'C_spine%02d_ctrlJntEnd' %
                      (spine_jnt_amount + 1))  # create end joint from head
        else:
            pm.delete(childJnt)
        i += 1

    # chest
    ########################
    jntsDic['chest01'] = {
        'ctrlJnt': pm.duplicate('C_chest01_skinJnt', n='C_chest01_ctrlJnt')[0]
    }
    pm.select(jntsDic['chest01']['ctrlJnt'])
    pm.mel.searchReplaceNames("_skinJnt", "_ctrlJnt", "hierarchy")

    pm.parent(jntsDic['chest01']['ctrlJnt'], 'C_spine_jntGrp')
    riggUtils.grpCtrl(jntsDic['chest01']['ctrlJnt'])

    # deal with children, make endJnt or delete
    i = 2
    for childJnt in pm.listRelatives(jntsDic['chest01']['ctrlJnt'],
                                     children=1,
                                     type='joint',
                                     ad=1):
        #print 'childJnt chest %s' % childJnt
        if childJnt == 'C_neck01_ctrlJnt':
            pm.rename(childJnt, 'C_chest02_endJnt')
        else:
            pm.delete(childJnt)

    # neck
    ########################
    jntsDic['neck01'] = {
        'ctrlJnt': pm.duplicate('C_neck01_skinJnt', n='C_neck01_ctrlJnt')
    }
    pm.select(jntsDic['neck01']['ctrlJnt'])  # rename
    pm.mel.searchReplaceNames("_skinJnt", "_ctrlJnt", "hierarchy")
    # parent n grp
    pm.parent(jntsDic['neck01']['ctrlJnt'], 'C_neck_jntGrp')
    riggUtils.grpCtrl(jntsDic['neck01']['ctrlJnt'])
    # deal with children, make endJnt or delete
    for childJnt in pm.listRelatives(jntsDic['neck01']['ctrlJnt'],
                                     children=1,
                                     type='joint',
                                     ad=1):
        #print 'childJnt neck %s' % childJnt
        if childJnt == 'C_head01_ctrlJnt':
            pm.rename(childJnt,
                      'C_neck02_ctrlJntEnd')  # create end joint from head
            jntsDic['neck02'] = childJnt
        else:
            pm.delete(childJnt)

    # head
    ########################
    jntsDic['head01'] = {
        'ctrlJnt': pm.duplicate('C_head01_skinJnt', n='C_head01_ctrlJnt')
    }
    pm.select(jntsDic['head01']['ctrlJnt'])  # rename
    pm.mel.searchReplaceNames("_skinJnt", "_ctrlJnt", "hierarchy")
    # parent n grp
    pm.parent(jntsDic['head01']['ctrlJnt'], 'C_head_jntGrp')
    riggUtils.grpCtrl(jntsDic['head01']['ctrlJnt'])
    # deal with children, make endJnt or delete
    i = 2
    for childJnt in pm.listRelatives(jntsDic['head01']['ctrlJnt'],
                                     children=1,
                                     type='joint',
                                     ad=1):
        #print 'childJnt head is %s' % childJnt
        if childJnt.split('_')[-1] == 'ctrlJnt':
            jntsDic['head%02d' % i] = childJnt
            riggUtils.grpCtrl(childJnt)
            i += 1
    # pelvis
    ########################
    jntsDic['pelvis01'] = {
        'ctrlJnt': pm.duplicate('C_pelvis01_skinJnt',
                                n='C_pelvis01_ctrlJnt')[0]
    }
    pm.select(jntsDic['pelvis01']['ctrlJnt'])
    pm.mel.searchReplaceNames("_skinJnt", "_ctrlJnt", "hierarchy")
    # parent n grp
    pm.parent(jntsDic['pelvis01']['ctrlJnt'], 'C_spine_jntGrp')
    riggUtils.grpCtrl(jntsDic['pelvis01']['ctrlJnt'])
    # delete legs, children of pelvis endJoint
    pm.delete(jntsDic['pelvis01']['ctrlJnt'].listRelatives(
        children=1)[0].listRelatives(children=1))
    lg.info('Done build_ctrlJnts_body')
    return jntsDic
예제 #8
0
 def grpCtrlsWin(self):
     for ctrl in pm.selected():
         mo_riggUtils.grpCtrl(ctrl)