motionPathノードのパラメーターを変換したい

motionPathノードをよく使うのですが、若干こまったことがありまして。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/e0a7db01-a2d9-b0c6-465e-7846b65c117e.png)


>パラメトリック長(Parametric Length)
Maya においてカーブ沿いにオブジェクトが移動するときの配置方法を指定します。パラメトリック空間とパラメトリック長の 2 つの方法があります。パラメトリック長(Parametric Length)を選択すると、パラメトリック長方法がアクティブになり、選択解除すると、パラメトリック空間方法がアクティブになります。
パラメトリック空間方法では、カーブの U パラメトリック空間における位置がマーカーによって表されます。パラメトリック長方法では、カーブの全長に対する割合(%)で位置がマーカーによって表されます。パラメトリック長方法は、割合のモード方法ともいいます。これは、パス カーブの長さの割合に基づいてパスの評価が行われるためです。
2 つの方法の違いを理解するため、パラメータ設定は均一であっても CV の間隔が均等でないカーブを考えてみます。


このパラメーター、位置をそのままで切り替えたい時がたまにありまして。
ただその場合、uValueについても変換してあげないと位置が動いてしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/90213ad0-bd61-2f23-2085-62b3cc525012.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/d2e82c56-8e1f-1f28-63ed-fc4f21c68f5d.png)

調査
--

**A:Parametric Length = True (fractionMode = False)**
カーブの全長に対する割合(%)

**B:Parametric Length = False (fractionMode = True)**
カーブ上のパラメーター位置

※アトリビュートエディタ上の値と、実際のアトリビュートの値が逆なので混乱しますね。

B に関しては、nearestPointOnCurveノードなどで取れそうな気はします。

問題はAに関してですが、
全体の長さは出せますが、カーブの始点から現在位置までの長さをどうにか求められれば・・・

困ったのでAPIを眺めます。

こんなのがありました
**OpenMaya.MFnNurbsCurve.findLengthFromParam()**

https://help.autodesk.com/view/MAYAUL/2022/JPN/?guid=Maya_SDK_py_ref_class_open_maya_1_1_m_fn_nurbs_curve_html

まさにこれですね欲していたのは。

実装
--

```python
import maya.cmds as cmds
import maya.api.OpenMaya as om



def getDagNode(target):    
    try:
        sellist = om.MGlobal.getSelectionListByName(target)
        return sellist.getDagPath(0)
    except:
        return None

def getShapeFn(dagPath):
    FnShape = None

    if dagPath.hasFn(om.MFn.kMesh):
        FnShape = om.MFnMesh(dagPath)        

    elif dagPath.hasFn(om.MFn.kNurbsSurface):
        FnShape = om.MFnNurbsSurface(dagPath)

    elif dagPath.hasFn(om.MFn.kNurbsCurve):
        FnShape = om.MFnNurbsCurve(dagPath)

    return FnShape


def convertToParametricPoint(motionPath):

    inputs = cmds.listConnections(motionPath + ".geometryPath",s =True,d =False) or []

    if len(inputs) == 0:
        return
    
    curve = inputs[0]
    dagPath = getDagNode(curve)
    shapeFn = getShapeFn(dagPath)


    curMode = cmds.getAttr(motionPath + ".fractionMode")

    ## getcur
    curPosition = cmds.getAttr(motionPath + ".allCoordinates")[0]
    curParam = shapeFn.getParamAtPoint(om.MPoint(curPosition))
    curLengthParam = shapeFn.findLengthFromParam(curParam) / shapeFn.length()

    if curLengthParam > 1.0:
        curLengthParam = 1.0


    if curMode:        
        cmds.setAttr(motionPath + ".fractionMode", 0)
        cmds.setAttr(motionPath + ".uValue", curParam)

    else:        
        cmds.setAttr(motionPath + ".fractionMode", 1)
        cmds.setAttr(motionPath + ".uValue", curLengthParam)

     
    cmds.refresh()
    return
    
```

この状態で実行してみると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/bd7f69c7-253a-7133-2702-acecbb6f780e.png)

上手くいってそうですね。
もう一回実行してみると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/38987c24-e374-94fc-3b25-75fb5c1ecf23.png)

元に戻りました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/c07bbcba-192d-d3ba-76f4-e1c94188c6e5.png)

何に使えるかはさておき、位置をキープしたまま切り替えができるようになりました。