C#ATIA

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

CustomGraphicsTextの闇が深すぎる1

こちらの原因と対策を探ってます。
計測をもっと実用的にしたい2 - C#ATIA

色々と悩み、思い付く事を試しつつ作ったものです。

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core

def run(context):
    ui: adsk.core.UserInterface = None
    try:
        app: adsk.core.Application = adsk.core.Application.get()
        ui = app.userInterface
        des: adsk.fusion.Design = app.activeProduct
        root: adsk.fusion.Component = des.rootComponent

        removeCG()

        billBoarding = False

        testData = [
            [
                'AB',
                adsk.core.Point3D.create(0,0,0),
                billBoarding
            ],
            [
                'CDE',
                adsk.core.Point3D.create(10,20,30),
                billBoarding
            ],
            [
                'FGHI',
                adsk.core.Point3D.create(-30,-20,-10),
                billBoarding
            ]
        ]

        for t, p, b in testData:
            initCGtext(root, t, p, b)

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

def initCGtext(
    comp: adsk.fusion.Component,
    txt: str,
    pnt: adsk.core.Point3D,
    isBillBoarding: bool
    ):

    vec: adsk.core.Vector3D = pnt.asVector()
    mat: adsk.core.Matrix3D = adsk.core.Matrix3D.create()
    mat.translation = vec

    cgGroup: adsk.fusion.CustomGraphicsGroup = comp.customGraphicsGroups.add()
    cgTxt: adsk.fusion.CustomGraphicsText = cgGroup.addText(
        txt,
        'Arial',
        3,
        mat)
    cgTxt.isBold = True
    cgTxt.isSelectable = False

    if isBillBoarding:
        billBoard = adsk.fusion.CustomGraphicsBillBoard.create(pnt)
        billBoard.billBoardStyle = adsk.fusion.CustomGraphicsBillBoardStyles.ScreenBillBoardStyle
        cgTxt.billBoarding = billBoard


    drawBBox(comp, cgTxt.boundingBox, txt)

def drawBBox(
    comp: adsk.fusion.Component,
    bBox: adsk.core.BoundingBox3D,
    name: str):

    oriented = toOriented(bBox)

    tmpMgr: adsk.fusion.TemporaryBRepManager = adsk.fusion.TemporaryBRepManager.get()
    box: adsk.fusion.BRepBody = tmpMgr.createBox(oriented)

    baseFeat : adsk.fusion.BaseFeature = comp.features.baseFeatures.add()
    baseFeat.startEdit()
    body: adsk.fusion.BRepBody = comp.bRepBodies.add(box, baseFeat)
    body.opacity = 0.5
    body.name = name
    baseFeat.finishEdit()

def toOriented(
    bBox: adsk.core.BoundingBox3D) -> adsk.core.OrientedBoundingBox3D:

    pMax: adsk.core.Point3D = bBox.maxPoint
    pMin: adsk.core.Point3D = bBox.minPoint

    center = adsk.core.Point3D.create(
        (pMax.x + pMin.x) * 0.5,
        (pMax.y + pMin.y) * 0.5,
        (pMax.z + pMin.z) * 0.5
    )

    return adsk.core.OrientedBoundingBox3D.create(
        center,
        adsk.core.Vector3D.create(1,0,0),
        adsk.core.Vector3D.create(0,1,0),
        abs(pMax.x - pMin.x),
        abs(pMax.y - pMin.y),
        abs(pMax.z - pMin.z)
    )

def removeCG():
    app: adsk.core.Application = adsk.core.Application.get()
    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()

これを実行するとこんなのが出来ます。
f:id:kandennti:20211021160930p:plain
"AB" 等の文字はCustomGraphicsTextで、近くの四角はサーフェスです。
この四角のサーフェスはCustomGraphicsTextのboundingBoxプロパティから
作り出しているので本当はこんな感じで出来上がるべきだと思ってます。
f:id:kandennti:20211021161552p:plain

自分の理解が足りないのか、バグなのか・・・。


続いて、一番悩んでいるのがBillBoardingです。
BillBoardingを利用すると、3D空間内をグルグル回しても文字が常に画面方向に
向いている状態を作ることが出来ます。
正に、計測時の数値がこの動きをします。

一部修正してこんな感じにします。

・・・
def run(context):
    ui: adsk.core.UserInterface = None
    try:
        app: adsk.core.Application = adsk.core.Application.get()
・・・

        billBoarding = True # <-ここ

        testData = [
・・・

修正後の物を実行するとこんな感じです。
f:id:kandennti:20211021162248p:plain
XY平面方向に見ると、さっきと変わらないんです。

ISO方向から見るとこんな感じ。
f:id:kandennti:20211021162428p:plain

原点に作成している "AB" は良いんです。画面グルグルしても大丈夫なんです。
"CDE" と "FGHI" は四角の位置からも離れちゃうんです。

別の角度から見てもおかしい。
(本当は動画の方が分かりやすい・・・)
f:id:kandennti:20211021162732p:plain

バグだと思いたい。何か計算式が有るのかな?


そもそも、ドキュメントに謎が多い。
・CustomGraphicsBillBoard.createメソッドのanchorPointの説明の
 "isViewDependentプロパティ" が見つからん。
Fusion 360 Help

・CustomGraphicsBillBoardオブジェクトのanchorPointの説明の
 "CustomGraphicsAnchorPointオブジェクトの静的createメソッドを使用して作成できます。"
 そんなオブジェクトは無い。(Point3Dになっている)
Fusion 360 Help

・CustomGraphicsText.boundingBoxプロパティの説明の
 ”グラフィックがモデル空間と画面空間のどちらで描画されているかに応じて、
 これはセンチメートル(モデル)またはピクセル(画面)のいずれかで
 バウンディングボックスを返します。” それどっちに描かれているかをどうやって知るの?
Fusion 360 Help


そもそもCustomGraphicsTextは、まだ不完全だったようなことが、以前フォーラムに
書いてあったような・・・。

計測をもっと実用的にしたい2

こちらの続きです。
計測をもっと実用的にしたい1 - C#ATIA

矢印は何とか出来ました・・・寸法値の表示で悩んでいます。
f:id:kandennti:20211021144355p:plain

緑はFusion360の計測です。僕が作ったのは赤の方。
文字が思っているところに表示されないんです。

困ったな。

計測をもっと実用的にしたい1

こちらに記載した内容です。
計測で最短距離を取得 - Autodesk Community

不満に感じているのが僕だけじゃなかった事に安心しました。
これを投げるきっかけはこちらです。
Solved: Measure minimum distance between two parallel faces - Autodesk Community

僕の理解不足でEkins氏が回答しちゃいましたが、負け惜しみを書くと
今朝、あの方法と同じことを思い付きました。
スピードが速いだけでは無く、オカレンスの要素にも対応出来ているので
大変魅力的な方法です。


と言う事で、計測をもっとCATIA風にしたいと考えています。
実際に測定する部分は何とかなるだろうと感じていますが、
見た目にもこだわりたい気持ちが強いです。

と言う事で、計測時に表示される寸法の矢印をまず再現したいです。
テスト中ですがこんな感じ。
f:id:kandennti:20211020165256p:plain
赤がFusion360の計測時の矢印で、緑がテスト中の矢印です。
ちょっとサイズが小さいかな?


フォーラムに大事な事を書き忘れました。
"今だけはモチベーションが高い。" のですが、3日後は分からない。

nonlocal

Fusion360APIでPythonを触るようになったのですが、
実はスコープについては未だに自信がない・・・。

"nonlocal" 知りませんでした。
Pythonのクロージャについて - Qiita

そもそも、globalを書かなくてもアクセス出来たりするところも
何となく違和感ある。

今は流行っているけど、10年後Pythonは残っているかな?

開発者向けアドイン1

昨日記載した、開発者向けアドインのUI
f:id:kandennti:20211019135043p:plain
劇的にセンスが無い。センスが欲しい。


赤印は開いているドキュメントを全て保存せずに終了します。
こちらの"全て閉じる"コマンドを移植。
GitHub - kantoku-code/Fusion360-CSSPP: Fusion360

緑印はコマンドIDの垂れ流し。
こちらを移植。
Fusion360_Small_Tools_for_Developers/CommandLogger at master · kantoku-code/Fusion360_Small_Tools_for_Developers · GitHub

青印はテキストコマンド関連で、これがメイン。
いつもコマンドを忘れちゃう(覚える気持ちが薄い)ので
ボタンにしちゃいたいと思ってました。

これも付けたいような・・・
Fusion360_Small_Tools_for_Developers/GetObjectInfo at master · kantoku-code/Fusion360_Small_Tools_for_Developers · GitHub


tapnairさんが、強力な開発者向けのアドイン公開しているんですけど。
GitHub - tapnair/f360dever: Development tools for Fusion 360
コマンドのボタンが無いのですが "ForgeCommands.py" が、Fusion360から
Forgeにアクセス出来そうな関数が用意されている。
使い道は思い付かないけど。

TextCommandsパレットをクリアする

Fusion360APIの開発者向け(要は僕用)のアドインを練習がてら作成中です。

前から、テキストコマンドのパレットをAPIでクリアしたいと思っていました。
こちらの "Window.Clear" がそうなんです。
Fusion360_Small_Tools_for_Developers/TextCommands_txt_Ver2_0_8176.txt at master · kantoku-code/Fusion360_Small_Tools_for_Developers · GitHub

これを手動で実行するとクリアされるのですが、APIではクリアしてくれないんです。
あぁ、まず汚さなと。

paths.get

を入力すると、いっぱい書き出されます。

実際にこんなスクリプトを実行してもクリアされません。

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core

def run(context):
    ui: adsk.core.UserInterface = None
    try:
        app: adsk.core.Application = adsk.core.Application.get()
        ui = app.userInterface

        app.executeTextCommand(u'window.Clear')

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

ところが、別の事をやっていて気が付いたのですが、executeTextCommand
の戻り値を受け取り、それをLogに垂れ流すとクリアしてくれました。

        res = app.executeTextCommand(u'window.Clear')
        app.log(res)

非常に不思議なのですが、目的は果たせました。

そこで戻り値は何なのかが気になり、ブレークポイントで止めて
"res" を見ると "TextCommandWindow.Clear" と言う文字列が入ってました。

それじゃ回りくどいことしないで、直接それを実行すればいいのかな?
と思い、この様にしました。

app.executeTextCommand(u'TextCommandWindow.Clear')

これ、駄目なんです。それはテキストコマンドには無いよって事らしいです。

で諦めようと思ったのですが、logに直接投げれば良いのでは?と思い

app.log(u'TextCommandWindow.Clear')

としたところ、クリアされました。

'TextCommandWindow.Clear' を受け入れてくれるこれは一体何だろう?

メッシュの六角形分割に挑む12

こちらの続きです。
メッシュの六角形分割に挑む11 - C#ATIA

色々な事をすっ飛ばしていますが、それなりに動くものをこちらに
添付しました。
Solved: Re: Dividing mesh into hexagonal sections - Autodesk Community

本当は操作性が悪くならないように、ダイアログを作って
動くようにしていたんです。UIはこんな感じです。
f:id:kandennti:20211015124551p:plain
ところが、全ての処理が終わったタイミングでFusion360がクラッシュ
してしまいます。

色々とやったつもりなのですが、どうしてもクラッシュが
回避できない為、ダイアログ無しで動くようにしました。
クラッシュの原因は不明なのですが、恐らくトランザクション
関係しているように感じます。

どうしてもダイアログを付けなければならないのであれば、pallet
UI作るしか無さそう。自信無いけど。