maya でハードエッジを転送したい

よし、ハードエッジの設定が終わったぞ。
あー・・・右側もか・・・・・ってなった場合に、

パッと思いつくやり方は

- 左のオブジェクトを複製して、反転させてリネーム
- 右のオブジェクトも左と同じになるように設定

あれ？ハードエッジ情報って転送できないんだっけ？

標準機能でできないか？
-

とりあえずできそうなのは、transfer Attributes かなぁ

こんな設定で
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/5ccd223b-b0b7-1916-e5c9-f5b371589c06.png)

こんなオブジェクトを
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/728123f7-bc50-e580-ffc3-8ff3a192b529.png)

あ、出来た
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/df20e21a-1488-5bd9-c7a4-24a3af39aeb6.png)
お、もうこれは終わりですかね？

もう１パターン、こんなオブジェクトを
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/48d1b2de-1d2a-663f-e179-e633616b7d75.png)

ん？間違ったかな？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/cf40d8a9-7cc8-b472-de99-4b3a1d001310.png)

ん？ん？
何をどうやっても転送できねぇ
他に出きそうな機能も見当たらないので、一旦切り替え。

※標準機能で転送する方法ご存じの方いらっしゃったら是非教えてください。

ハードエッジ情報
-
ハードエッジ情報はどこに格納されているのかなぁー
先述のテストで転送出来たってことはvertexNormalと関係してるとは思うのですが、それは完全ではない。
つまりハードエッジ情報は別途どこかに格納されとるんではなかろうか。

と仮定。

https://help.autodesk.com/cloudhelp/2022/JPN/Maya-Tech-Docs/CommandsPython/polySoftEdge.html

>エッジは、所有する 2 つのフェース間の角度が、スムージング角度より鋭角(大きい)の場合にハードになります。
>所有する 2 つのファセット間の角度が、スムージング角度より鈍角(小さい)場合はソフトになります。

そっかーフェースかー
フェースノーマルはvtxノーマルで定義されるしなぁ・・・
失敗したケースは平面だったから？？？

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

**OpenMaya.MFnMesh.isEdgeSmooth()**
あ、こんなのもありますね。

ということはエッジ毎に　ハードorソフト　が確認できるようですね。

エッジからエッジへ
-
ならば、こうだ。

- 転送元オブジェクトのハードなエッジIDを取得
- 転送先オブジェクトのハードエッジを全部ソフト化
- 転送先オブジェクトでエッジIDを元にハードを設定

こんな流れで。


- ハードなエッジIDの取得
まぁとりあえず総当たりで

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

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 getHardEdges(target):
    dagPath = getDagNode(target)
    FnShape = getShapeFn(dagPath)
    edgeNum = FnShape.numEdges

    hardIDs = []
    for edgeID in range(0,edgeNum):
        if FnShape.isEdgeSmooth(edgeID)==False:            
            hardIDs.append(edgeID)

    return hardIDs
```

- ハードエッジを全部ソフト化
まぁ全部ソフトエッジ化すればいいんですわよね

とおもったけど、transformノード指定してpolySoftEdgeで行けますね。

```python
cmds.polySoftEdge(target, ch = False,angle =180)
```

- エッジIDを元にハードを設定

```python
import maya.cmds as cmds

def setHardEdge(target,edgeIDs):    
    targetEdges = []
    for edgeID in edgeIDs:
        targetEdges.append(target + ".e["+str(edgeID)+"]")

    cmds.polySoftEdge(targetEdges, ch = False,angle =0)
```

これらを組み合わせて


```python
import maya.api.OpenMaya as om
import maya.cmds as cmds
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 getHardEdges(target):
    dagPath = getDagNode(target)
    FnShape = getShapeFn(dagPath)
    edgeNum = FnShape.numEdges

    hardIDs = []
    for edgeID in range(0,edgeNum):
        if FnShape.isEdgeSmooth(edgeID)==False:            
            hardIDs.append(edgeID)

    return hardIDs

def setHardEdge(target,edgeIDs):    
    targetEdges = []
    for edgeID in edgeIDs:
        targetEdges.append(target + ".e["+str(edgeID)+"]")

    cmds.polySoftEdge(targetEdges, ch = False,angle =0)

def transferHardEdge(source, target):
    edgeIDs = getHardEdges(source)
    cmds.polySoftEdge(target, ch = False,angle =180)
    setHardEdge(target,edgeIDs)

```

先ほどの失敗ケースでテスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/f2de9852-4da6-62d1-23b1-9293551b03b0.png)

問題なく転送出来た様子。

そういえば
-

終わりじゃない。
１つここで思い出したことが

見た目が完全に一緒でも faceのIDが異なる場合がある
vtxIDが完全に一致しててもfaceのIDが異なる場合がある

これedgeでもあり得るのかな？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/dd1d35d5-e31f-ed45-5849-591e9a658080.png)

はい　あり得ました。
1本エッジを削除して、connectComponentでつなぎ直してみたところ
edgeIDはずれました。


エッジをvtxにして、vtxからエッジに
-
edge貼り直しても幸い vtxIDは変わらないので、
vtxIDのorderは一致しているというのを最低限の条件として再考。

- 転送元オブジェクトのハードなedgeを取得
- エッジを構成しているvtxIDを取得
- 転送先オブジェクトでvtxIDで構成されるedgeIDを取得
- 転送先オブジェクトのハードエッジを全部ソフト化
- 転送先オブジェクトでedgeIDをハードを設定

こんなところでしょうか。

- エッジを構成しているvtxIDを取得

こんな感じで
```python
def getEdgeContainVtx(target,edgeIDs):    
    dagPath = getDagNode(target)
    FnShape = getShapeFn(dagPath)
    edgeVtxDict = {}
    
    for edgeID in edgeIDs:
        vtexIds = FnShape.getEdgeVertices(edgeID)        
        edgeVtxDict[edgeID] = vtexIds

    return edgeVtxDict

```

- 転送先オブジェクトでvtxIDで構成されるedgeIDを取得

vtxに接続しているedgeは複数本あるので、
それらのうちで　指定したvtxすべてに接続されているedgeを探せばいいかしら？

```python
def convertVtexToEdge(target,edgeVtxDict):
    dagPath = getDagNode(target)
    FnShape = getShapeFn(dagPath)        
    it = om.MItMeshVertex(dagPath)
    convertedDict = {}

    for key in list(edgeVtxDict.keys()):
        shareEdges = []

        for vtexID in edgeVtxDict[key]:    
            it.setIndex(vtexID)
            edges = it.getConnectedEdges()

            if len(shareEdges) == 0:
                shareEdges = edges
            else:    
                shareEdges = list(set(shareEdges) & set(edges))

        if len(shareEdges) == 0:
            continue

        for shareEdge in shareEdges:
            convertedDict[shareEdge] = edgeVtxDict[key]
              
    return convertedDict
```

まとめる
-
まとめるとこんな感じ

```python
import maya.api.OpenMaya as om
import maya.cmds as cmds
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 getHardEdges(target):
    dagPath = getDagNode(target)
    FnShape = getShapeFn(dagPath)
    edgeNum = FnShape.numEdges

    hardIDs = []
    for edgeID in range(0,edgeNum):
        if FnShape.isEdgeSmooth(edgeID)==False:            
            hardIDs.append(edgeID)

    return hardIDs

def setHardEdge(target,edgeIDs):    
    targetEdges = []
    for edgeID in edgeIDs:
        targetEdges.append(target + ".e["+str(edgeID)+"]")

    cmds.polySoftEdge(targetEdges, ch = False,angle =0)

def getEdgeContainVtx(target,edgeIDs):    
    dagPath = getDagNode(target)
    FnShape = getShapeFn(dagPath)
    edgeVtxDict = {}
    
    for edgeID in edgeIDs:
        vtexIds = FnShape.getEdgeVertices(edgeID)        
        edgeVtxDict[edgeID] = vtexIds

    return edgeVtxDict

def convertVtexToEdge(target,edgeVtxDict):
    dagPath = getDagNode(target)
    FnShape = getShapeFn(dagPath)        
    it = om.MItMeshVertex(dagPath)
    convertedDict = {}

    for key in list(edgeVtxDict.keys()):
        shareEdges = []

        for vtexID in edgeVtxDict[key]:    
            it.setIndex(vtexID)
            edges = it.getConnectedEdges()

            if len(shareEdges) == 0:
                shareEdges = edges
            else:    
                shareEdges = list(set(shareEdges) & set(edges))

        if len(shareEdges) == 0:
            continue

        for shareEdge in shareEdges:
            convertedDict[shareEdge] = edgeVtxDict[key]
              
    return convertedDict

def transferHardEdge(source, target):
    edgeIDs = getHardEdges(source)
    edgeVtxDict = getEdgeContainVtx(source,edgeIDs)
    
    cmds.polySoftEdge(target, ch = False,angle =180)
    convertedDict = convertVtexToEdge(target,edgeVtxDict)    
    setHardEdge(target,list(convertedDict.keys()))
    
sel = cmds.ls(sl =True)
transferHardEdge(sel[0], sel[1])

```

vtxIDは一致してるけど、edgeID順がズレてるコレでテスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/9cf4f1bf-4fa4-560e-bb35-56506c9fc0b5.png)

よし、うまく行きました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/255b4a0f-f6a1-82d1-4f15-401e3185b5ba.png)