2011/01/25

非平面を平面に2

前回の続き。
以前のものは選択したポリゴン面の法泉を基準(これは少し怪しい)に平面化してましたが
今回はスクリプトを実行するとポイントをピックさせられます。
3点ピックするとそれを元に平面化します。
相変わらず4つ以上の面に接したポイントがあるとダメです。
いやぁ使えない使えない。。。

from win32com.client import *
xsi = Application

def GetReferencePoints():
    ''' リファレンスポイントを取得(最低3点) '''
    xsi.ActivateVertexSelTool()
    lPnts = []
    while len(lPnts) < 3:
        rtn = xsi.PickElement(constants.siPointFilter)
        oPick = rtn[2]
        if not oPick:  #ピックが足りない場合処理中止
            return
        for oCmp in oPick.SubComponent.ComponentCollection:
            lPnts.append(oCmp)
    return lPnts

def GetReferenceTransform(lPnts):
    ''' リファレンストランスフォームを取得 '''
    #外積を取得
    v1 = XSIMath.CreateVector3()
    v2 = XSIMath.CreateVector3()
    vCrs = XSIMath.CreateVector3()
    v1.Sub(lPnts[1].Position, lPnts[0].Position)
    v2.Sub(lPnts[2].Position, lPnts[0].Position)
    vCrs.Cross(v1, v2)
    #トランスフォームに変換(後でレイキャストさせるために位置をどのポイントとも違う位置(ポイントの中点)に)
    vPos = XSIMath.CreateVector3()
    for i in range(3):
        vPos.AddInPlace(lPnts[i].Position)
    vPos.ScaleInPlace(1.0/len(lPnts))
    vYAxis = XSIMath.CreateVector3(0,1,0)
    oTrans = XSIMath.CreateTransform()
    oTrans.Translation = vPos
    nAgl   = vYAxis.Angle(vCrs)
    vCrs.Cross(vCrs, vYAxis)
    vCrs.NegateInPlace()
    oTrans.SetRotationFromAxisAngle(vCrs, nAgl)
    return oTrans

def MovePointToReferenceTransform(oFace, oItemTrans, oRefTrans, oAllPnts):
    ''' ポイントをリファレンストランスフォーム上のY=0に移動 '''
    lPosArr = [list(t) for t in oAllPnts.PositionArray]
    for oPnt in oFace.Points:
        vPosOnRef = XSIMath.MapObjectPositionToObjectSpace(oItemTrans, oRefTrans, oPnt.Position)
        vPosOnRef.Y = 0
        vPosOnRef.ScaleInPlace(10)
        vPos = XSIMath.MapObjectPositionToObjectSpace(oRefTrans, oItemTrans, vPosOnRef)
        lPosArr[0][oPnt.Index] = vPos.X
        lPosArr[1][oPnt.Index] = vPos.Y
        lPosArr[2][oPnt.Index] = vPos.Z
    oAllPnts.PositionArray = lPosArr
        
def GetEdgeInfo(oFace):
    ''' エッジ情報を取得 '''
    
    def GetInfo(oEdge, lPntIndxsOnFace):
        oPnts = oEdge.Points
        if oPnts[0].Index in lPntIndxsOnFace:
            oStartPnt = oPnts[1]
            oEndPnt   = oPnts[0]
        else:
            oStartPnt = oPnts[0]
            oEndPnt   = oPnts[1]
        vStart = oStartPnt.Position
        vEnd   = oEndPnt.Position
        vEnd.SubInPlace(vStart)
        vStart.SubInPlace(vEnd)
        return vStart, vEnd, oEndPnt.Index
        
    oNgbrEdges = oFace.Edges.NeighborEdges()
    lPntIndxsOnFace = [oPnt.Index for oPnt in oFace.Points]
    lPos    = [] #開始位置リスト
    lAxis   = [] #方向リスト
    lPntIdx = [] #対象ポイントインデックスリスト
    for oEdge in oNgbrEdges:
        lInfo = GetInfo(oEdge, lPntIndxsOnFace)
        lPos.extend(lInfo[0].Get2())
        lAxis.extend(lInfo[1].Get2())
        lPntIdx.append(lInfo[2])
    return lPos, lAxis, lPntIdx
    
def MovePointToRaycastPosition(oGeom, oFace, lEdgeInfo):
    ''' ポイントをレイキャストした先に移動 '''
    oGeom.SetupPointLocatorQueries(3, None, [oFace.Index], -1)
    oPntLocations = oGeom.GetRaycastIntersections(lEdgeInfo[0], lEdgeInfo[1], 0)
    lPos = oGeom.EvaluatePositions(oPntLocations)
    oPnts = oGeom.Points
    lPosArr = [list(t) for t in oPnts.PositionArray]
    for idx, allidx in enumerate(lEdgeInfo[2]):
        lPosArr[0][allidx] = lPos[0][idx]
        lPosArr[1][allidx] = lPos[1][idx]
        lPosArr[2][allidx] = lPos[2][idx]
    oPnts.PositionArray = lPosArr
    
def main():
    oSel = xsi.Selection(0)
    if oSel.Type != 'polySubComponent': #ポリゴンが選択されてない場合処理中止
        return
    #選択ポリゴンのサブコンポーネントからいろいろ取得
    oSubCmp    = oSel.SubComponent
    oFace      = oSubCmp.ComponentCollection[0]
    oPoly      = oFace.Nodes[0]
    oItem      = oSubCmp.Parent3DObject
    xsi.FreezeModeling(oItem)                              #フリーズしとかないとポイントの移動ができない
    oItemTrans = oItem.Kinematics.Global.Transform
    oGeom      = oItem.ActivePrimitive.Geometry
    oAllPnts   = oGeom.Points
    lPosArr    = [list(t) for t in oAllPnts.PositionArray]
    oAllPnts.PositionArray = lPosArr                       #一旦ポイントをいじっとくことで初回実行からちゃんと動く(なぞ)
    lEdgeInfo  = GetEdgeInfo(oFace)                        #エッジ情報を保存
    lPnts      = GetReferencePoints()                      #参照トランスフォーム作成用ポイントをピックさせる(最低3点)
    if not lPnts:
        return
    oRefTrans  = GetReferenceTransform(lPnts)              #参照トランスフォームを作成
    MovePointToReferenceTransform(oFace, oItemTrans, oRefTrans, oAllPnts) #とりあえず平面化
    MovePointToRaycastPosition(oGeom, oFace, lEdgeInfo)                   #できた面と保存しておいてエッジをレイキャストして補正
    xsi.ActivateRaycastPolySelTool()
    
main()

0 件のコメント:

コメントを投稿