C#ATIA

↑タイトル詐欺 主にCATIA V5 の VBA(最近はPMillマクロとFusion360APIが多い)

マウスカーソルの座標値を取得する3

こちらの続きです。
マウスカーソルの座標値を取得する2 - C#ATIA

前回はマウスカーソルの3D位置は正しい値が取得できていませんでしたが、
今回は正しい値が取得出来ているはずです。

#FusionAPI_python
#Author-kantoku
#Description-MouseMoveTest ver0.0.3

import adsk.core, adsk.fusion, traceback

_ui  = None
_handlers = []
_faces = []
_covunit = 0

#このコマンドのイベント・ダイアログ作成
class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            global _handlers
            cmd = adsk.core.Command.cast(args.command)

            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onMouseMove = MyMouseMoveHandler()
            cmd.mouseMove.add(onMouseMove)
            _handlers.append(onMouseMove)

            inputs = cmd.commandInputs
            
            inputs.addTextBoxCommandInput('Vp_Pos', 'Fusion360画面上の座標値', '-', 1, True)
            inputs.addTextBoxCommandInput('Sc_Pos', 'ディスプレイ上の座標値', '-', 1, True)
            inputs.addTextBoxCommandInput('3D_Pos', 'マウス3D仮座標値', '-', 1, True)
            inputs.addTextBoxCommandInput('Cam_Vec', 'カメラ向き(単位ベクトル)', '-', 1, True)
            inputs.addTextBoxCommandInput('Hit', 'マウス3D座標値', 'Non!', 1, True)
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
            
#このコマンドの破棄
class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            adsk.terminate()
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

#MouseMoveイベント
class MyMouseMoveHandler(adsk.core.MouseEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.MouseEventArgs.cast(args)
        cmd = eventArgs.firingEvent.sender
        inputs = cmd.commandInputs
        
        #ビューポイント
        vp = eventArgs.viewport
        
        #Fusion360画面上の座標値
        vppos = eventArgs.viewportPosition
        ui_vp = inputs.itemById('Vp_Pos')
        ui_vp.text = 'x:[%d] y:[%d]'%(vppos.x, vppos.y)
        
        #ディスプレイ上の座標値
        scrpos = vp.viewToScreen(vppos)
        ui_sc = inputs.itemById('Sc_Pos')
        ui_sc.text = 'x:[%d] y:[%d]'%(scrpos.x, scrpos.y)
        
        #マウス3D仮座標値-奥行きがNGな為正しくない(通過点)
        d3pos = vp.viewToModelSpace(vppos)
        d3_sc = inputs.itemById('3D_Pos')
        d3_sc.text = 'x:{:.3f} y:{:.3f} z:{:.3f}'.format(
            d3pos.x * _covunit, 
            d3pos.y * _covunit, 
            d3pos.z * _covunit)
        
        #カメラ向き(単位ベクトル)
        cam = vp.camera
        vec = cam.eye.vectorTo(cam.target)
        vec.normalize()
        vec_sc = inputs.itemById('Cam_Vec')
        vec_sc.text = 'x:{:.3f} y:{:.3f} z:{:.3f}'.format(vec.x, vec.y, vec.z)

        #マウス3D座標値
        hit_sc = inputs.itemById('Hit')
        onface = OnFace(vp, eventArgs.viewportPosition)
        if onface == None:
            hit_sc.text = 'Non!'
        else:
            hit_sc.text = 'x:{:.3f} y:{:.3f} z:{:.3f}'.format(
                onface[1].x * _covunit,
                onface[1].y * _covunit,
                onface[1].z * _covunit)

#マウスカーソルの3D取得
def OnFace(vp, vp_pos):
    d3pos = vp.viewToModelSpace(vp_pos)
    cam = vp.camera
    vec = cam.eye.vectorTo(cam.target)
    
    pnt = adsk.core.Point3D.create(
        d3pos.x + vec.x, 
        d3pos.y + vec.y, 
        d3pos.z + vec.z)
        
    mouse3d = adsk.core.Line3D.create(d3pos, pnt).asInfiniteLine()    
    
    ints = [(face, mouse3d.intersectWithSurface(geo))
            for (face, geo) in _faces if mouse3d.intersectWithSurface(geo).count > 0]
    
    ints = [(face, p) 
            for (face, pnts) in ints
            for p in pnts]

    ints = [(face, p, face.evaluator.getParameterAtPoint(p))
            for (face, p) in ints]
    
    ints = [(face, p, prm)
            for (face, p, (res, prm)) in ints if res]
    
    ints = [(face, p, prm)
            for (face, p, prm) in ints]
            
    ints = [(face, p, cam.eye.distanceTo(p))
            for (face, p, prm) in ints if face.evaluator.isParameterOnFace(prm)]
    
    if len(ints) < 1:
        return None
    
    return min(ints, key = (lambda x: x[2]))
    
def run(context):
    try:
        app = adsk.core.Application.get()
        _ui = app.userInterface
                
        cmdDef = _ui.commandDefinitions.itemById('Mouse_Move_Test')
        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                'Mouse_Move_Test', 
                'Mouse_Move_Test', 
                'Mouse_Move_Test')

        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)
        
        des = adsk.fusion.Design.cast(app.activeProduct)
        
        #全サーフェス取得
        global _faces
        _faces = [(face, face.geometry)
            for comp in des.allComponents if comp.isBodiesFolderLightBulbOn
            for bBody in comp.bRepBodies if bBody.isLightBulbOn & bBody.isVisible
            for face in bBody.faces]

        #単位準備
        global _covunit
        unitsMgr = des.unitsManager
        defLenUnit = unitsMgr.defaultLengthUnits
        _covunit = unitsMgr.convert(1, unitsMgr.internalUnits, defLenUnit)


        cmdDef.execute()

        adsk.autoTerminate(False)
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

コードが汚い。特にOnFace関数。
スプリクト実行後、ダイアログが出ますが一番下の項目が正確であろう3D座標値です。
マウスカーソルが面の上に無い時は "Non!" と表示されています。

確認しやすそうな数値でBodyを作り、マウスを動かし
確認してみました。

動画の最後の方ですが、スプリクトを中止しマウスを動かしているのですが
(要は何のコマンドにも入っていない状態です)、マウスカーソル下の
面がハイライト状態になります。(スプリクトではなりません)
・・・と言うことはFusion360自体でどの面の上にマウスカーソルが
認識しているのではないかな? と感じているのですが・・・。
現状、ファイル内の全ての面との交差を取得し、複数交差した点と
カメラとの距離を測定し、最短となった点の座標値を取得し表示させています。
効率が非常に悪いんです。どうしたらこの面を取得できるのだろう???