C#ATIA

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

オフセット面の作成場所2

こちらの続きです。
オフセット面の作成場所 - C#ATIA

前回の最後に記載した
コンポーネントからオカレンスをどうやったら取得できるのか?”
のテストです。

ややこしいのですが、こんな状態のデータでテストします。
f:id:kandennti:20181002130440p:plain
青部分がルートコンポーネント
赤Bodyを最初に選択してテスト(root - コンポーネント1:1 のBody)
緑Bodyを後で選択してテスト(root - コンポ1:1 - コンポ2:1 - コンポ3:1 のBody)

このようなコードを用意しました。

#FusionAPI_python test_offset
#Author-kantoku
#Description-オフセット面の作成

import adsk.core, adsk.fusion, traceback
 
def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        #選択
        sel = Sel('Select Base Body','SolidBodies')
        if sel is None:
            return
        
        #選択したボディ
        bd = sel.entity
        
        #ボディからコンポーネント
        comp = bd.parentComponent
        print('--start--')
        print('// 選択した ボディ名 - コンポ名 //')
        print('{}-{}'.format(bd.name,comp.name))
        
        #作業中のデザイン(作業中のファイルみたいなもの)
        des = adsk.fusion.Design.cast(app.activeProduct)
        
        #ルートコンポーネント(全体のコンポーネント)
        root = des.rootComponent
        
        #コンポーネントのメソッド・プロパティ
        comps = root.allOccurrencesByComponent(comp)
        print('// allOccurrencesByComponent //')
        [DumpOccInfo(o) for o in comps]
        
        comps = root.occurrencesByComponent(comp)
        print('// occurrencesByComponent //')
        [DumpOccInfo(o) for o in comps]        
        
        comps = comp.occurrences
        print('// occurrences //')
        [DumpOccInfo(o) for o in comps]  

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

#オカレンス情報
def DumpOccInfo(occ):
    print('--occ info --\nocc name : {}\n子Occ数 : {}\nコンポ名 : {}\n'
        .format(occ.name,len(occ.childOccurrences),occ.component.name))

#選択
def Sel(msg, selFilter):
    app = adsk.core.Application.get()
    ui  = app.userInterface
    try:
        return ui.selectEntity(msg, selFilter)
    except:
        return None

コンポーネントクラスにはオカレンスが関連しそうなメソッド・プロパティが
3個ありました。
・allOccurrencesByComponent(メソッド)
・occurrencesByComponent(メソッド)
・occurrences(プロパティ)
正直なところ、APIのHelp読んでも意味がわからないのでテストした
ってことです。 通常 "プロパティでしょ" とは思っています。


赤Bodyを選択した結果はこちら

--start--
// 選択した ボディ名 - コンポ名 //
Body1-コンポーネント1
// allOccurrencesByComponent //
--occ info --
occ name : コンポーネント1:1
子Occ数 : 2
コンポ名 : コンポーネント1

// occurrencesByComponent //
--occ info --
occ name : コンポーネント1:1
子Occ数 : 2
コンポ名 : コンポーネント1

// occurrences //
--occ info --
occ name : コンポーネント2:1
子Occ数 : 1
コンポ名 : コンポーネント2

--occ info --
occ name : コンポーネント4:1
子Occ数 : 0
コンポ名 : コンポーネント4

--done--

赤Bodyを選択した際に欲しいのは、"コンポーネント1:1" です。
occurrences(プロパティ)で得られるのは、そのコンポーネント
ぶら下がっているコンポーネント(子コンポーネント)です。

"コンポーネント3:1" が得られていないので、孫以下は見ていないです。
※ならば、ChildrenOccurrenceのような名称の方がわかりやすい!!


緑Bodyを選択した結果はこちら

--start--
// 選択した ボディ名 - コンポ名 //
Body1-コンポーネント3
// allOccurrencesByComponent //
--occ info --
occ name : コンポーネント3:1
子Occ数 : 0
コンポ名 : コンポーネント3

// occurrencesByComponent //
// occurrences //
--done--

occurrences(プロパティ)については、子供のオカレンスが無い為
出力無しは納得です。

occurrencesByComponent(メソッド)の出力無しについては
ルートコンポーネントから見て、緑Bodyの入っているオカレンスが
ひ孫の位置にあるため得られないため、出力無しとなるのだろうと
思います。(あくまで対象は子供のオカレンスのみ)

allOccurrencesByComponent(メソッド)は "all" が付いている
だけあって呼び出したコンポーネント(今回はルートコンポーネント
以下のオカレンスであれば取得できるようです。
※よく見たらallOccurrences(プロパティ)もありましたが、
 動作は察しが付きますし、目的のものでも無さそうです。


で、探した限り直接コンポーネントから該当するプロパティは
無さそうな気がしています。
その為、拡張メソッド用の関数を用意し昨日のオフセットスプリクトを
修正しました。

#FusionAPI_python test_offset2
#Author-kantoku
#Description-オフセット面の作成

import adsk.core, adsk.fusion, traceback
 
def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        des = adsk.fusion.Design.cast(app.activeProduct)
        
         #拡張
        adsk.fusion.Component.toOcc = toOccurrenc
        
        sel = Sel('Select Solid Face','SolidFaces')
        if sel is None:
            return
            
        face = sel.entity
        CreateZeroOffset(face)
        
        ui.messageBox('Done')
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
            
def Sel(msg, selFilter):
    app = adsk.core.Application.get()
    ui  = app.userInterface
    try:
        return ui.selectEntity(msg, selFilter)
    except:
        return None
    
def CreateZeroOffset(face):
    zero = adsk.core.ValueInput.createByString('0 cm')
    newBody = adsk.fusion.FeatureOperations.NewBodyFeatureOperation
    
    #選択された面のあるボディのコンポーネント取得
    comp = face.body.parentComponent
    
    #コンポーネントのフューチャーのオフセットを取得
    offsets = comp.features.offsetFeatures
    
    objs = adsk.core.ObjectCollection.create()
    objs.add(face)
    
    occ = comp.toOcc()
    if not occ is None:
        occ.activate()
    offsets.add(offsets.createInput(objs, zero, newBody))
    
#adsk.fusion.Component 拡張メソッド
def toOccurrenc(self):
    root = self.parentDesign.rootComponent
    if self == root:
        return None
        
    occs = [occ
            for occ in root.allOccurrencesByComponent(self)
            if occ.component == self]
    return occs[0]

意図した位置にオフセット面が作成出来るようになりました。
本来であれば実行前のコンポーネントをアクティブにして終了したいの
ですが、ルートではオカレンスが取得できない為、オフセット面を
作った状態で終了と、雑な処理になっています。


Fusion360APIの場合、コンポーネントとオカレンスは密接な関係だと
思っていますが、特にオカレンスに関してはわかりにくいのが本音です。
知っていることを含めまとめると

・ルートコンポーネントにはオカレンスが無い。
 (ルート自体は固定されていて、移動等出来ない為だと思う)

・Treeに "コンポーネント" と書かれているものは実はオカレンス
 nameプロパティを取得した感じだと、こんなイメージ。
f:id:kandennti:20181002130518p:plain
 名前が異なる為、「itemByName」では取得できない。

・"コンポーネント" は各コンポーネントを作業しているような状態で
 ルートから見た際(移動等位置決めしている場合)オカレンスとして
 扱う必要がある。

・オカレンスは重要なのにBRep~(例えばBRepBody)には、直接
 オカレンスを取得する方法が無い。(と思う)
 parentComponentメソッドはあるけど、parentOccurrenceは無い。

・手動の "コンポーネントのアクティブ化" はAPIの場合、
 Occurrence.activate() になるようですが、ルートコンポーネント
 オカレンスが無いのでどうやれば良いのだろうか?

Helpも "コンポーネント" と説明している部分が圧倒的多数なのですが
実際はオカレンスを説明している部分もありそうな感じです。