draggerContext　を使ってみた

カーソルの軌跡に沿ってナーブスカーブを編集してみたかったので、
draggerContextを使ってやってみた。

まずはdraggerContextを下調べ
-

https://help.autodesk.com/cloudhelp/2019/JPN/Maya-Tech-Docs/CommandsPython/draggerContext.html

**projection**

- viewPlane (ビュー プレーンに投影)
- objectViewPlane ((ビュー プレーンと平行な)オブジェクト プレーンに投影)
- objectPlane (オブジェクトの位置と法線(既定)0,1,0 で定義した指定プレーンに投影)
- plane	(原点と法線(既定)0,1,0 で定義した指定プレーンに投影)
- sketchPlane	(スケッチ プレーンに投影)
- xAxis	(X 軸上の最近接ポイントに投影)
- yAxis	(Y 軸上の最近接ポイントに投影)
- zAxis	(Z 軸上の最近接ポイントに投影)
- boundingSphere	(オブジェクト球の境界上の最近接ポイントに投影)
- boundingBox	(オブジェクト バウンディング ボックス上の最近接ポイントに投影)

この辺がよく判らないので、細々検証したい。
ざっくりと検証用のツールを作成

    import maya.cmds as cmds
    def pressCommand_test():
        pressPosition = cmds.draggerContext(toolName, query=True, anchorPoint=True)    
        print("start position" + str(pressPosition))

    def dragCommand_test():
        dragPosition = cmds.draggerContext( toolName, query=True, dragPoint=True)
        print(str(dragPosition))
        node = cmds.spaceLocator()[0]
        cmds.setAttr(node + ".t", *dragPosition)
    
    def releaseCommand_test():
        pass
    
    toolName = "hogeTool"

    if cmds.draggerContext(toolName,q =True, exists=True):
        cmds.deleteUI(toolName)
    
    cmds.draggerContext(toolName,
                        projection = "plane",
                        pressCommand = "pressCommand_test()",
                        dragCommand = "dragCommand_test()",
                        releaseCommand = "releaseCommand_test()",
                        space = "world"
    )

    cmds.setToolTo(toolName)

perspカメラ越しに実行してみて、結果を確認

**viewPlane**
現在のカメラのviewPlane上に投影し、3次元上の位置情報を取得
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/55be7835-2e36-501d-0842-697618b827e7.png)

**objectViewPlane**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/95fa09b8-6687-99ab-6c8a-eeaefbe23edf.png)

**objectPlane / plane**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/99f43475-9a35-c3b0-022f-6c419bab6506.png)

オプションを変えてみて判ったことは

- space を **wolrd**にしているので3次元上の座標になるが、**screen**にするとこでビューポートの左下を0，0としたX,Y座標を取得できる

- **オブジェクト**の指定方法がよく変わらない


その上でnurbsCurveをどうこうしたい
-

どうこうしたいので、どうしよう。
ざっくりと考えるとこんな感じかしら？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/137779a6-672d-237b-80f3-cae0a76701a0.png)

カメラ = A  CV 元位置 = B  ストローク上 = C  CV 移動先 = P　と置きなおすと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/0dfd84b4-edd4-dad0-3366-38e1ab7e74ad.png)

ベクトルAC を　ABと同じ長さになるように延長した点をPとする

必要になってくるのは

- **A:カメラ位置**
これで現在のカメラを取得できそう。
OpenMayaUI.M3dView.active3dView().getCamera()

        cam_MFnTransIns = om2.MFnTransform(om2.MFnDagNode(omui.M3dView.active3dView().getCamera().transform()).getPath())
        cam_position = cam_MFnTransIns.translation(om2.MSpace.kWorld)


- **C:viewPlane上のストローク座標**
draggerContext で　projecction = viewPlane かつ　space = world で取得可能

- **B:CVの元位置**
xfromあたりで取得


点Cの定義
-

ストローク上の点は沢山取得できるけれども、どれを点Ｃとして定義しようか？

- ストロークの各ポイントからパラメーター位置を割り出す

- CVのworld位置をビューポート上の2次元位置に変換して、ストロークの最近接点を割り出す

- ストローク上の点を採用してnurbsCurveを生成、rebuildで元Curveと諸々合わせてCV位置を取得

軌跡の描画も兼ねさせてしまいたいので、3つ目を採用
pressCommand
 - ストロークのカーブがあったら削除
 - クリックした点を取得

draggCommand
 - ストロークのカーブが無ければ作成
 - ある場合はポイントをappendしていく
 
releaseCommand
 - 最初に選択していたカーブに合わせて　degree / spans を取得し、rebuild


    pressPosition = []
    tmpCurveName = "tempCurve"
    selectCurve = ""
    toolName = "hogeTool"

    def draggerEditCurve_pressCommand():
        global pressPosition
        pressPosition = cmds.draggerContext(toolName, query=True, anchorPoint=True)    
        if cmds.objExists(tmpCurveName):
            cmds.delete(tmpCurveName)

    def draggerEditCurve_dragCommand():
        dragPosition = cmds.draggerContext( toolName, query=True, dragPoint=True)

        if cmds.objExists(tmpCurveName):
            cmds.curve(tmpCurveName , p = [dragPosition], append =True)
        else:
            cmds.curve(name = tmpCurveName , p = [pressPosition,dragPosition], d = 1, bezier = False, periodic = False)
    
        cmds.refresh()

    def draggerEditCurve_releaseCommand(selectCurve):    
        degree = cmds.getAttr(selectCurve + ".degree")
        spans = cmds.getAttr(selectCurve + ".spans")    
        cmds.rebuildCurve(tmpCurveName,ch =False, degree = degree,rebuildType = 0,spans = spans,replaceOriginal = True,keepRange =0)
    
    def draggerEditCurveTool():
        selectCurve = cmds.ls(sl =True)[0]
        
        if cmds.draggerContext(toolName,q =True, exists=True):
            cmds.deleteUI(toolName)
        
            cmds.draggerContext(toolName,
                            projection = "viewPlane",
                            pressCommand = "draggerEditCurve_pressCommand()",
                            dragCommand = "draggerEditCurve_dragCommand()",
                            releaseCommand = "draggerEditCurve_releaseCommand(\""+selectCurve+"\")",
                            space = "world"
        )
        
        cmds.setToolTo(toolName)

    draggerEditCurveTool()


点Pの定義
-

2種類のカーブとカメラの位置から、最終的な移動地点を定義する。

    def getDistPoint(sourceCurve,targetCurve):
        cam_MFnTransIns = om2.MFnTransform(om2.MFnDagNode(omui.M3dView.active3dView().getCamera().transform()).getPath())
        cam_position = om2.MVector(cam_MFnTransIns.translation(om2.MSpace.kWorld))    
        sourceCurve_dagPath = om2.MGlobal.getSelectionListByName(sourceCurve).getDagPath(0)
    
        if sourceCurve_dagPath.hasFn(om2.MFn.kNurbsCurve):
            sourceCurve_shapeFn = om2.MFnNurbsCurve(sourceCurve_dagPath)
    
        all_ids = []
        for i in range(0,sourceCurve_shapeFn.numCVs):
            all_ids.append(i)
    
        sourceCurve_comp = [sourceCurve + '.cv[' + str(vid) + ']'for vid in all_ids]
        targetCurve_comp = [targetCurve + '.cv[' + str(vid) + ']'for vid in all_ids]

        for sourceCurve_cv,targetCurve_cv in zip(sourceCurve_comp,targetCurve_comp):
            sourceCurve_cv_position = om2.MVector(cmds.xform(sourceCurve_cv,q =True,ws =True, t=True))
            targetCurve_cv_position = om2.MVector(cmds.xform(targetCurve_cv,q =True,ws =True, t=True))
            curPoint_vect = targetCurve_cv_position - cam_position
            curPoint_length = curPoint_vect.length()
            point_vect = sourceCurve_cv_position - cam_position
            point_vect = point_vect.normal()
            dist_vect = point_vect * curPoint_length
            dist_position = cam_position + dist_vect
            cmds.xform(targetCurve_cv,ws =True, t=dist_position)


結合
-
細々と作ってきたものをまとめるとこんな感じ。
なにかしらカーブを選択して実行。

    import maya.cmds as cmds
    import maya.api.OpenMaya as om2
    import maya.api.OpenMayaUI as omui
    
    pressPosition = []
    tmpCurveName = "tempCurve"
    toolName = "draggerEditCurveTool"
    
    def getDistPoint(sourceCurve,targetCurve):
        cam_MFnTransIns = om2.MFnTransform(om2.MFnDagNode(omui.M3dView.active3dView().getCamera().transform()).getPath())
        cam_position = om2.MVector(cam_MFnTransIns.translation(om2.MSpace.kWorld))        
        sourceCurve_dagPath = om2.MGlobal.getSelectionListByName(sourceCurve).getDagPath(0)
        
        if sourceCurve_dagPath.hasFn(om2.MFn.kNurbsCurve):
            sourceCurve_shapeFn = om2.MFnNurbsCurve(sourceCurve_dagPath)
        
        all_ids = []
        for i in range(0,sourceCurve_shapeFn.numCVs):
            all_ids.append(i)
        
        sourceCurve_comp = [sourceCurve + '.cv[' + str(vid) + ']'for vid in all_ids]
        targetCurve_comp = [targetCurve + '.cv[' + str(vid) + ']'for vid in all_ids]
    
        for sourceCurve_cv,targetCurve_cv in zip(sourceCurve_comp,targetCurve_comp):
            sourceCurve_cv_position = om2.MVector(cmds.xform(sourceCurve_cv,q =True,ws =True, t=True))
            targetCurve_cv_position = om2.MVector(cmds.xform(targetCurve_cv,q =True,ws =True, t=True))
            curPoint_vect = targetCurve_cv_position - cam_position
            curPoint_length = curPoint_vect.length()
            point_vect = sourceCurve_cv_position - cam_position
            point_vect = point_vect.normal()
            dist_vect = point_vect * curPoint_length
            dist_position = cam_position + dist_vect
            cmds.xform(targetCurve_cv,ws =True, t=dist_position)
    
    def draggerEditCurve_pressCommand():
        global pressPosition
        pressPosition = cmds.draggerContext(toolName, query=True, anchorPoint=True)    
        if cmds.objExists(tmpCurveName):
            cmds.delete(tmpCurveName)
    
    def draggerEditCurve_dragCommand():
        dragPosition = cmds.draggerContext( toolName, query=True, dragPoint=True)
    
        if cmds.objExists(tmpCurveName):
            cmds.curve(tmpCurveName , p = [dragPosition], append =True)
        else:
            cmds.curve(name = tmpCurveName , p = [pressPosition,dragPosition], d = 1, bezier = False, periodic = False)
        cmds.refresh()
    
    def draggerEditCurve_releaseCommand(selectCurve):    
        degree = cmds.getAttr(selectCurve + ".degree")
        spans = cmds.getAttr(selectCurve + ".spans")    
        cmds.rebuildCurve(tmpCurveName,ch =False, degree = degree,rebuildType = 0,spans = spans,replaceOriginal = True,keepRange =0)    
        getDistPoint(tmpCurveName,selectCurve)    
        cmds.select(selectCurve,r =True)
        cmds.delete(tmpCurveName)
            
    def draggerEditCurveTool():
        selectCurve = cmds.ls(sl =True)[0]
            
        if cmds.draggerContext(toolName,q =True, exists=True):
            cmds.deleteUI(toolName)
            
        cmds.draggerContext(toolName,
                                projection = "viewPlane",
                                pressCommand = "draggerEditCurve_pressCommand()",
                                dragCommand = "draggerEditCurve_dragCommand()",
                                releaseCommand = "draggerEditCurve_releaseCommand(\""+selectCurve+"\")",
                                space = "world"
            )
            
        cmds.setToolTo(toolName)
        
    draggerEditCurveTool()


現行だとopen以外のカーブは対応してなかったりとか色々限定的ではあるので、後々アップデートしたいなぁ