C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

複数面に線を描きたい1

ちょっと前にこちらを公開しました。
GitHub - kantoku-code/Fusion360_DrawCurveOnSurface: Fusion360 DrawCurve OnSurface
指定した面上にクリックした境界・頂点を結ぶ線を描くアドインです。
地味な感じで何度か修正入れてます・・・。

フォーラムの方にも書いたのですが、このアドインの一番の欠点は
複数面にわたって線を描くことが出来ないんです。
(境界・頂点以外を指定出来ない事も・・・)

複数面に結ぶための処理方法が良くわからないんで、連日調べているものの
糸口が見つからない。誰かに相談したいものの誰にどんな内容で相談して
良いのかすら見当がつかないです。


そこで突貫でサーフェスのUVを可視化する為のスクリプトを作りました。
・・・紫外線じゃない方のUVです。

#FusionAPI_python Script
#Author-kantoku
#Description-マウスカーソル部分のUVを可視化

import adsk.core, adsk.fusion, traceback

_app = adsk.core.Application.cast(None)
_ui  = adsk.core.UserInterface.cast(None)
_handlers = []
_fact = None

# 俺コマンド用情報
_cmdInfo = ['UVviewer','UVviewer','UVviewer']

# SelectionCommandInput用情報
_selSktInfo = ['dlgSel','何も選択出来ません','何も選択出来ません']

class CommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            adsk.terminate()
        except:
            if _ui:
                _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


class PreSelectHandler(adsk.core.SelectionEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args):
        try:
            args = adsk.core.SelectionEventArgs.cast(args)
            args.isSelectable = False

            sel = args.selection
            ent = sel.entity
            entType :str = ent.objectType.split('::')[-1]

            global _fact
            if entType == 'BRepFace':
                _fact.setFace(ent)
            else:
                _fact.setFace(None)

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

class MouseMoveHandler(adsk.core.MouseEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args :adsk.core.MouseEventArgs):
        try:
            global _fact
            _fact.setMouse(
                args.viewport,
                args.viewportPosition)

            _fact.update()
            args.viewport.refresh()

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


class CommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            # -- Command --
            global _ui, _handlers
            cmd = adsk.core.Command.cast(args.command)
            cmd.isPositionDependent = True
            cmd.isOKButtonVisible = False

            # -- Event --
            onDestroy = CommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            # MouseMove
            onMouseMove = MouseMoveHandler()
            cmd.mouseMove.add(onMouseMove)
            _handlers.append(onMouseMove)

            # preSelect
            onPre = PreSelectHandler()
            cmd.preSelect.add(onPre)
            _handlers.append(onPre)

            # -- dialog --
            global _selInfo
            inputs = cmd.commandInputs
            inputs.addSelectionInput(_selSktInfo[0], _selSktInfo[1], _selSktInfo[2])

            # -- other --
            global _fact
            _fact = DrawUVFactry()

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

class DrawUVFactry():
    _vp = adsk.core.Viewport.cast(None) 
    _face = adsk.fusion.BRepFace.cast(None)
    _clone = adsk.fusion.BRepFace.cast(None)
    _musPos = adsk.core.Point2D.cast(None)
    _cgGroup = adsk.fusion.CustomGraphicsGroup.cast(None)

    red = adsk.core.Color.create(255,0,0,255)
    _solidRed = adsk.fusion.CustomGraphicsSolidColorEffect.create(red)
    blue = adsk.core.Color.create(0,0,255,255)
    _solidBlue = adsk.fusion.CustomGraphicsSolidColorEffect.create(blue)

    def __init__(self):
        self.refreshCG()

    def __del__(self):
        self.removeCG()

    def removeCG(self):
        des :adsk.fusion.Design = _app.activeProduct
        cgs = [cmp.customGraphicsGroups for cmp in des.allComponents]
        cgs = [cg for cg in cgs if cg.count > 0]
        
        if len(cgs) < 1: return

        for cg in cgs:
            gps = [c for c in cg]
            gps.reverse()
            for gp in gps:
                gp.deleteMe()

    def refreshCG(self):
        self.removeCG()
        des :adsk.fusion.Design = _app.activeProduct
        root :adsk.fusion.Component = des.rootComponent
        self._cgGroup = root.customGraphicsGroups.add()

    def setMouse(self, vp :adsk.core.Viewport, musPos :adsk.core.Point2D):
        self._vp = vp
        self._musPos = musPos

    def setFace(self, face :adsk.fusion.BRepFace):
        if self._face == face:
            return

        self._face = face

        if face:
            tmpBrep = adsk.fusion.TemporaryBRepManager.get()
            cloneBody = tmpBrep.copy(face)
            self._clone = cloneBody.faces[0]
        else:
            self._clone = None

    def update(self):
        self.refreshCG()

        if not self._face: return

        pos3d = self._vp.viewToModelSpace(self._musPos)
        cam = self._vp.camera
        vec = cam.eye.vectorTo(cam.target)
    
        musPnt = adsk.core.Point3D.create(
            pos3d.x + vec.x, 
            pos3d.y + vec.y, 
            pos3d.z + vec.z)
        
        musInf =  adsk.core.Line3D.create(pos3d, musPnt).asInfiniteLine()

        geo :adsk.core.Surface = self._clone.geometry
        
        ints = musInf.intersectWithSurface(geo)
        if ints.count < 1: return

        pnt :adsk.core.Point3D = min(ints, key = (lambda p:cam.eye.distanceTo(p)))

        eva :adsk.core.SurfaceEvaluator = self._clone.evaluator
        res, prm = eva.getParameterAtPoint(pnt)

        # print('{}:{}'.format(prm.x,prm.y))
        crvsU = eva.getIsoCurve(prm.x, False)
        crvsV = eva.getIsoCurve(prm.y, True)

        for crv in crvsU:
            crvCg = self._cgGroup.addCurve(crv)
            crvCg.color = self._solidRed
            crvCg.weight = 2

        for crv in crvsV:
            crvCg = self._cgGroup.addCurve(crv)
            crvCg.color = self._solidBlue
            crvCg.weight = 2

def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface
        des :adsk.fusion.Design = _app.activeProduct
        root :adsk.fusion.Component = des.rootComponent

        cmdDefs :adsk.core.CommandDefinitions = _ui.commandDefinitions

        global _cmdInfo
        cmdDef :adsk.core.CommandDefinition = cmdDefs.itemById(_cmdInfo[0])
        if cmdDef:
            cmdDef.deleteMe()

        cmdDef = cmdDefs.addButtonDefinition(_cmdInfo[0],_cmdInfo[1],_cmdInfo[2])

        global _handlers
        onCommandCreated = CommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)
        cmdDef.execute()

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

こんな感じです。
f:id:kandennti:20200506231348p:plain
マウスカーソルをキャプチャするのが面倒な為写ってませんが、赤と青が
交差している部分にマウスカーソルがあります。

ボディ上にマウスカーソルを置くとその位置のUVラインを表示します。
(恐らく)赤がUで青がVです。

そのうち、他人には役立たないと思われる小さなスクリプト類を、まとめて
置いておくところをGithubに作ろう。とても手元で管理しきれ無さそう。


この技術を何とか手に入れたい。狙っているのはスケッチのラインをボディに
ラップさせる処理です。こちらに公開されているものがあるのですが、
GitHub - hanskellner/Fusion360WrapSketch: Wrap sketch curves around a cylinder
制約が多い上、開発が止まってます。
昔、コードを読んだ際の記憶だと、全て丁寧に座標値を計算させて
ラッピングさせていたようですが、その方法だと確かに限界があります。
単一面であれば、僕は他の方法で出来る事を既に知っていますよ。