C#ATIA

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

NativeObjectとProxyの理解とモロモロ1

Fusion360で僕が一番悩むのが、コンポーネントに対応させたスクリプト(とアドイン)です。
APIフォーラムでも比較的最近は少なくなりましたが、コンポーネント(正しくはOccurrence)
絡みで "正しい結果が得られない" "バグじゃないのか?" のようなものの原因は恐らくこれです。

今後、Fusion360APIを取り組まれる方の為に知っている範囲で記載しておきます。
対象は初心者を脱したかな? 辺りです(それは僕)
通常であれば "履歴をキャプチャする" モードで行うはずなので、それを前提でお話を進めます。


構築-オフセット平面でXY平面+30mmの平面を作成します。
続いて、オフセット平面をサポートにしてスケッチを作成し、X 10mm Y 20mmを中心とする
円を描きます。こんな感じです。(直径は無関係です)
f:id:kandennti:20201126113020p:plain
この円の中心点は、X 10mm Y 20mm Z 30mm として存在しているはずですよね?
測定コマンドを使用してもご覧の通りです。
f:id:kandennti:20201126113033p:plain
これをスプリクトで取得し表示させるために、この様なものを作成しました。

# Fusion360API Python script
# sample1
import adsk.core, adsk.fusion, traceback

def run(context):
    ui = None
    try:
        # おまじない
        app :adsk.core.Application = adsk.core.Application.get()
        ui :adsk.core.UserInterface = app.userInterface
        des :adsk.fusion.Design = app.activeDocument.design

        # ルートコンポーネント
        root :adsk.fusion.Component = des.rootComponent

        # コンポーネント内の1個目のスケッチ
        skt :adsk.fusion.Sketch = root.sketches[0]

        # スケッチ内の1個目の円 - 目的の円
        curvrs :adsk.fusion.SketchCurves = skt.sketchCurves
        circle :adsk.fusion.SketchCircle = curvrs.sketchCircles[0]

        # SketchCircleには中心点情報が無いので、geometryを取得
        geo :adsk.core.Circle3D = circle.geometry

        # 単位を正しくしたい為、係数取得
        unitsMgr :adsk.core.UnitsManager = des.unitsManager
        defLenUnit :str = unitsMgr.defaultLengthUnits
        covunit :float = unitsMgr.convert(1, unitsMgr.internalUnits, defLenUnit)

        # 中心座標表示
        ctr :adsk.core.Point3D = geo.center
        ui.messageBox('X: {1}{0}\nY: {2}{0}\nZ: {3}{0}'.format(
            defLenUnit, ctr.x * covunit, ctr.y * covunit, ctr.z * covunit))

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

これを実行すると・・・
f:id:kandennti:20201126113046p:plain
裏切られました。Zの値が正しくありません。
原因はこちらです。

・・・

        # SketchCircleには中心点情報が無いので、geometryを取得
        geo :adsk.core.Circle3D = circle.geometry # <-ここ

・・・

円(SketchCircleオブジェクト)には中心の情報が無いため、幾何学的情報を得るためには
ジオメトリ(Circle3Dオブジェクト)を取得し調べることになりますが、

geometryプロパティ:スケッチ内での状態
worldGeometryプロパティ:スケッチを抜けた状態

で取得する事になります。
Fusion 360 Help

つまりこの様に修正します。

・・・

        # SketchCircleには中心点情報が無いので、geometryを取得
        geo :adsk.core.Circle3D = circle.worldGeometry # <-こんな感じ

・・・

これで実行すると
f:id:kandennti:20201126113059p:plain
無事、目的の値で取得出来ました。worldGeometryプロパティを使用しなくても
正しい値を取得する方法はあるのですが、これが一番間違いない方法だと思います。
・・・コンポーネントを使用しなければ ですが。



次にコンポーネントを使用した場合を見ていきます。
まず、新規にコンポーネントを作成します。
外部だとややこしいので "内部" にしましょう。
f:id:kandennti:20201126113110p:plain
予め言葉の整合性を保つため、青矢印部分は "ルートコンポーネント" と呼ぶことにします。
APIでそうなっているからです)
この状態の場合、ルートコンポーネントと新作のコンポーネントの原点位置は同じです。

続いて新作のコンポーネント内にXY平面をサポートしたスケッチを作成して、スケッチの原点を
中心に円を描きます。
その後スケッチを抜け、移動コマンドでコンポーネントを移動させます。
f:id:kandennti:20201126113123p:plain
X 10mm Y 20mm Z 30mm としておきましょう。

移動コマンドを終了した際、注意が必要です。
通常、ツールパネルに一番右側にこちらが表示されます。
f:id:kandennti:20201126113133p:plain
"位置をキャプチャ" を必ずさせて位置を確定させます。
確定後、ルートコンポーネントをアクティブにすると履歴に追加されます。
これを行わないと何を行っても、移動前の情報しか取得できません。
(以前、悩まされました。 これはAPIでも確定させることは可能です。)
・・・今気が付きましたが、移動コマンドのダイアログ内に "位置をキャプチャ" の
チェックボックスが有ったんですね。皆使ってないと思うけど。


さて、この状態で円の中心座標を取得する場合、当然ルートコンポーネントから見た状態の
X 10mm Y 20mm Z 30mm を期待します。
f:id:kandennti:20201126113145p:plain

この様に、コンポーネントに対応した状態にスクリプトを修正しました

# Fusion360API Python script
# sample2
import adsk.core, adsk.fusion, traceback

def run(context):
    ui = None
    try:
        # おまじない
        app :adsk.core.Application = adsk.core.Application.get()
        ui :adsk.core.UserInterface = app.userInterface
        des :adsk.fusion.Design = app.activeDocument.design

        # ルートコンポーネント
        root :adsk.fusion.Component = des.rootComponent

        # ルートコンポーネント内のオカレンスの1個目
        occs :adsk.fusion.Occurrences = root.allOccurrences
        occ :adsk.fusion.Occurrence = occs[0]

        # オカレンスのコンポーネント
        # オカレンスにはスケッチが無く、参照元のコンポーネントに有り
        comp :adsk.fusion.Component = occ.component

        # コンポーネント内の1個目のスケッチ
        skt :adsk.fusion.Sketch = comp.sketches[0]

        # スケッチ内の1個目の円 - 目的の円
        curvrs :adsk.fusion.SketchCurves = skt.sketchCurves
        circle :adsk.fusion.SketchCircle = curvrs.sketchCircles[0]

        # SketchCircleには中心点情報が無いので、geometryを取得
        geo :adsk.core.Circle3D = circle.worldGeometry

        # 単位を正しくしたい為、係数取得
        unitsMgr :adsk.core.UnitsManager = des.unitsManager
        defLenUnit :str = unitsMgr.defaultLengthUnits
        covunit :float = unitsMgr.convert(1, unitsMgr.internalUnits, defLenUnit)

        # 中心座標表示
        ctr :adsk.core.Point3D = geo.center
        ui.messageBox('X: {1}{0}\nY: {2}{0}\nZ: {3}{0}'.format(
            defLenUnit, ctr.x * covunit, ctr.y * covunit, ctr.z * covunit))

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

この状態で実行すると・・・
f:id:kandennti:20201126113158p:plain
あっさり期待を裏切られます。先程のworldGeometryプロパティを使っているにも関わらず。
"ちっともWorldじゃねぇじゃないか!” と、お思いになった方、正解です。

worldGeometryは、スケッチから抜けた状態で取得しているのは間違いないです。
しかし抜けた状態なだけで、スケッチの所属しているコンポーネントでの円を
取得しているにすぎません。例えるのであれば、新作したコンポーネント
アクティブにした状態と考えて良いでしょう。
f:id:kandennti:20201126113211p:plain
念のためお伝えしておきますが、ルートコンポーネントをアクティブにした状態でも
裏切られます。


じゃあどうすれば・・・と言う事になりますが長くなりすぎた為、本題に入らないまま
次回に続けます。