お知らせ

オートデスク コミュニティ フォーラムが新しくなりました。変更点については、コミュニティ掲示板のお知らせをご覧ください。

板状に分割したい

loverinlen
Advocate

板状に分割したい

loverinlen
Advocate
Advocate

少々分かりにくい説明になるかもしれません

 

添付画像のような立体物を

厚み3mmの板で構成されているようなものにしたいです。

スクリーンショット 2022-07-28 16.33.55.pngスクリーンショット 2022-07-28 16.35.04.png

 

シェルで厚み3mmにする所まではいいのですが

それぞれ面で分割された板状のものにしたいです。

その板同士をくっつけたら立体になる感じです。

スクリーンショット 2022-07-28 16.38.53.png

 

最初は辺全てにスケッチの線を描き、分割すればいいと思ったのですが

それだと底面まで余計に分割してしまいます。

スクリーンショット 2022-07-28 16.47.19.pngスクリーンショット 2022-07-28 16.53.28.png

 

そもそも分割する角度が思った角度でもなく…

理想としてはこい言う間を取った分割、かつ底面は分割しない…というものです

スクリーンショット 2022-07-28 16.49.13.pngスクリーンショット 2022-07-28 16.52.56.png

本来はもっと面が多く、いろんな角度がついたものを制作予定で、これは簡易的な図になりますが…

とても分かりにくい質問になりました。すみません

何かいい方法がある方教えていただけますと幸いです。

一応このデータは載せておきます。すでに暑さ3mmにはなっています。

よろしくお願いいたします。

0 件のいいね
返信
解決済み
1,620件の閲覧回数
32件の返信
返信 (32)

adachitech7
Consultant
Consultant
解決済み

@loverinlen さん、

 

こんな感じで作ってみました。

キャプチャ.PNG

 

始めに天面と底面を分割しておいてから側面を分割しています。隣接する面同士の中立面を作ってボディ分割し、余計なところを分割してしまったものは後で結合しました。

 

天面と側面との間に中立面を作って「面の置換」コマンドを使ってぴったり合うようにしています。

動画をご確認ください。

 

kandennti
Mentor
Mentor

@loverinlen さん こんにちは。

 

ロフトコマンドを使って試した所、上手く行ったので・・・。

 

最初にボディをコピーします。

その後、ロフトで外側の面と内側の面(選択時クリック長押しです)を指定し、

交差にします。

1.png

 

これを外側(又は内側)の枚数分の繰り返しです。

 

平らな面で作成されていれば、均等な肉厚でなくても出来そうな

気はしています。(未確認)

loverinlen
Advocate
Advocate

なるほど、ありがとうございます!

いい感じにできました

しかしやはり辺なところで分割されてしまうのは、手作業で直していくしかないんですね…

本当に作りたい形はだいぶ多いので大変そうですが、ちまちまやってみます

ありがとうございます!

0 件のいいね

loverinlen
Advocate
Advocate

すみません。よくわからないのですが…

シェルで全部とりあえず5mm厚にしたところの、外側と内側ってことでしょうか?

長押しとは何のことですか?

0 件のいいね

kandennti
Mentor
Mentor
解決済み

@loverinlen さん

 

>外側と内側ってことでしょうか?

ロフトを作る際に外側と内側の面で作ると言う事です。

 

>長押しとは何のことですか?

内側の面の選択は直接は行えない為、選択する際の操作方法です。

動画内で"面・面・面・面"のメニューが出ている際の操作です。

 
面白そうなテーマなので、今スクリプトを作ってます。

kandennti
Mentor
Mentor

よく考えたら、ロフトの操作は "交差" じゃなくて "新規ボディ" でOK

のような気がします。そうすると元のボディのコピペも要らない・・・。

loverinlen
Advocate
Advocate

おぉ!なるほど

ありがとうございます!

これなら比較的楽に作れそうですね

長押し操作は初めて知りました。とても便利そうです!

ボディをコピーしないで新規ボディも試してみます

 

スクリプトいいですね!たのしみですw

 

相談に乗っていただき、ありがとうございます!

0 件のいいね

kandennti
Mentor
Mentor

@ChaosComb さん

 

このテーマでスプリクト挑戦してみますか?

それであれば、こちらには直接僕が作ったものは記載しないようにしますが。

 

"外側-内側"の組み合わせをどんな風に見つけるか?" が

結構キモだと思うのですが。

ChaosComb
Advisor
Advisor

@kandennti 

 

ご指名いただきありがとうございます!

挑戦してみます...

とは言いつつ,できるかどうかわからないことに @loverinlen さんを巻き込むのも申し訳ないですし, @kandennti さんのプログラムも見て勉強したい感もあるのでお気になさらず投稿なさってください!

自分もいい感じになったら投稿しますw


ChaosComb
Advisor
Advisor

@loverinlen さん, @kandennti さん

 

とりあえずテストで作ってみました.

普通のシェルコマンドの要領で,対象となるボディと厚みを入力すれば分割されたボディが生成されます.

(なので,シェルを実行する前の状態のボディの状態でプログラムを実行してください.)

 

ChaosComb_0-1659457368011.png

 

#Author-
#Description-

import adsk.core, adsk.fusion, adsk.cam, traceback

_app: adsk.core.Application = None
_ui: adsk.core.UserInterface = None

_handlers = []

_selIpt: adsk.core.SelectionCommandInput = None
_tabIpt: adsk.core.SelectionCommandInput = None
_thickness = 0

def run(context):
    ui = None
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        cmdDef: adsk.core.CommandDefinition = _ui.commandDefinitions.itemById(
            'Shell_and_Loft'
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                'Shell_and_Loft',
                'Shell_and_Loft',
                'Shell_and_Loft'
            )

        global _handlers
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)

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

class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.CommandCreatedEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)
        try:
            global _handlers

            cmd: adsk.core.Command = adsk.core.Command.cast(args.command)

            # OKボタンの表示を変更
            cmd.okButtonText = 'Create'

            # イベント登録
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            # 実行ボタンのコントロールイベント登録
            onValidateInputs = MyValidateInputsHandler()
            cmd.validateInputs.add(onValidateInputs)
            _handlers.append(onValidateInputs)

            # OKボタンを押した際のイベント
            onExecute = MyExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)

            # 入力窓
            inputs: adsk.core.CommandInputs = cmd.commandInputs

            global _selIpt, _tabIpt, _thickness

            _selIpt = inputs.addSelectionInput(
                '_selIpt1',
                'SolidBody',
                'SolidBody'
            )
            # フィルターを設定する
            _selIpt.addSelectionFilter('Bodies')
            # リミットの設定
            _selIpt.setSelectionLimits(1, 1)

            # Create a tab input.
            _thickness = inputs.addValueInput(
                'Thickness',
                'Thickness',
                'mm',
                adsk.core.ValueInput.createByReal(0.0))

            # Connect to the command destroyed event.
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

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

# OKボタンのコントロール
class MyValidateInputsHandler(adsk.core.ValidateInputsEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args: adsk.core.ValidateInputsEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)

        # 1ボディ,厚みとして正の値が入力されているときに実行可能
        global _selIpt, _thickness
        if not ((_selIpt.selectionCount == 1) and ((_thickness.value) > 0)):
            args.areInputsValid = False
            return

# OKボタン選択後
class MyExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.CommandEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)

        # ダイアログ上のSelectionCommandInput
        global _thickness

        product = _app.activeProduct
        design = adsk.fusion.Design.cast(product)
        rootComp = design.rootComponent
        features = rootComp.features

        # シェル前に既存の面情報を取得
        body_info = _app.activeProduct.allComponents[0].bRepBodies[0]
        face_info = body_info.faces

        face_ID_out = []
        face_vDirec_out = []
        for i in range(face_info.count):
            face_ID_out.append(face_info[i].tempId)
            face_vDirec_out.append(face_info[i].geometry.vDirection)

        # シェルの実行
        entities1 = adsk.core.ObjectCollection.create()
        entities1.add(body_info)
        shellFeats = features.shellFeatures
        isTangentChain = False
        shellFeatureInput = shellFeats.createInput(entities1, isTangentChain)
        thickness = adsk.core.ValueInput.createByReal(_thickness.value)
        shellFeatureInput.insideThickness = thickness
        shellFeats.add(shellFeatureInput)

        # シェルにより生成した新規の面情報を取得
        face_ID_in = []
        face_vDirec_in = []
        for i in range(face_info.count):
            ID = face_info[i].tempId
            if not ID in face_ID_out:
                face_ID_in.append(ID)
                face_vDirec_in.append(face_info[i].geometry.vDirection)
        
        # 探索方法:シェルにより生成される面に振られるIDと既存のIDには関係性がありそうなのでそれを利用

        for i in range(len(face_ID_out)):
            for j in range(face_info.count):
                if face_ID_out[i] == face_info[j].tempId:
                    profile1 = face_info[j]
                elif face_ID_in[i] == face_info[j].tempId:
                    profile2 = face_info[j]
            loftFeats = rootComp.features.loftFeatures
            loftInput = loftFeats.createInput(adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
            loftSectionsObj = loftInput.loftSections
            loftSectionsObj.add(profile1)
            loftSectionsObj.add(profile2)
            loftInput.isSolid = True
            loftInput.isClosed = False
            loftInput.isTangentEdgesMerged = True
            loftFeats.add(loftInput)

class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.CommandEventArgs):
        adsk.terminate()

 

ただ,どうしようかな,と悩んでいる部分がありまして...

例えば以下のようにブロックの上にブロックが詰まれている状態の時

 

ChaosComb_1-1659457514230.png

 

このプログラムを実行すると,特定の部分でロフトにエラーが発生します

ブロック同士のつなぎ目の部分ですね

穴が開いているプロファイルなのでロフトが失敗してしまいます.

 

ChaosComb_2-1659457594174.png

 

もし,こういう形状の者は対象としない!となればいい線言ってるかな...という感じなのですがどこか悔しい感じもあったり.

進捗報告でした.

要望,質問等ございましたらご連絡ください.

 

 

 


kandennti
Mentor
Mentor

@ChaosComb さん

無茶ぶりでスイマセン。 @loverinlen さんが添付してくれたデータで

処理したかったので、シェルは既にされた状態を想定していました。

 

@loverinlen さん

こちらに外面-内面をロフトさせてボディを作るスクリプトを作ってみました。

https://kantoku.hatenablog.com/entry/2022/08/02/153816

あちらに記載していますが、処理をする条件としては、
・完全中空なボディな事
・全ての面が平らな面で構成されている事
と制限しています。

 

又、出来上がるボディは、ボディ毎にコンポーネントになっており、

(見た目を派手にするためです)単純にボディになっていれば良いの

あれば、1行だけ書き換えて下さい。

ChaosComb
Advisor
Advisor

@loverinlen さん

 

昨日からの進捗です.

ブロックが詰まれているような状態の時でも対応できるようになりました.(図1 ⇒ 図2)

満足度合はぐっとあがりました

(追記) @kandennti さんのプログラムと同様,平面にしか対応していません.

と思っていたのですができるかもしれません.(できない形状もあるかも,フィレットした面程度ならできました)

 

ChaosComb_0-1659500130403.png

 

ChaosComb_1-1659500177297.png

 

 

 

#Author-
#Description-

import adsk.core, adsk.fusion, adsk.cam, traceback

_app: adsk.core.Application = None
_ui: adsk.core.UserInterface = None

_handlers = []

_selIpt: adsk.core.SelectionCommandInput = None
_tabIpt: adsk.core.SelectionCommandInput = None
_thickness = 0

def create_by_Loft(profile1_set, profile2_set, n):
    try:
        product = _app.activeProduct
        design = adsk.fusion.Design.cast(product)
        rootComp = design.rootComponent
        features = rootComp.features

        tool_body = adsk.core.ObjectCollection.create()
        for i in range(n):
            if not (n == 1):
                profile1 = profile1_set(i)
                profile2 = profile2_set(i)
            else:
                profile1 = profile1_set
                profile2 = profile2_set            
            loftFeats = rootComp.features.loftFeatures
            loftInput = loftFeats.createInput(adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
            loftSectionsObj = loftInput.loftSections
            loftSectionsObj.add(profile1)
            loftSectionsObj.add(profile2)
            loftInput.isSolid = True
            loftInput.isClosed = False
            loftInput.isTangentEdgesMerged = True
            loftFeats.add(loftInput)

            if i == 0:
                target_body = _app.activeProduct.allComponents[0].bRepBodies[-1]
            else:
                tool_body.add(_app.activeProduct.allComponents[0].bRepBodies[-1])
        
        if not (n == 1):
            # 外と中で別に作成したボディを結合(切り取り)
            combineFeats = rootComp.features.combineFeatures
            combileInput = combineFeats.createInput(target_body, tool_body)
            combileInput.isNewComponent = False
            combileInput.isKeepToolBodies = False
            combileInput.operation = adsk.fusion.FeatureOperations.CutFeatureOperation
            combineFeats.add(combileInput)

    except:
        adsk.terminate()

def run(context):
    ui = None
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        cmdDef: adsk.core.CommandDefinition = _ui.commandDefinitions.itemById(
            'Shell_and_Loft'
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                'Shell_and_Loft',
                'Shell_and_Loft',
                'Shell_and_Loft'
            )

        global _handlers
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)

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

class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.CommandCreatedEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)
        try:
            global _handlers

            cmd: adsk.core.Command = adsk.core.Command.cast(args.command)

            # OKボタンの表示を変更
            cmd.okButtonText = 'Create'
            cmd.cancelButtonText = 'Cancel'

            # イベント登録
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            # 実行ボタンのコントロールイベント登録
            onValidateInputs = MyValidateInputsHandler()
            cmd.validateInputs.add(onValidateInputs)
            _handlers.append(onValidateInputs)

            # OKボタンを押した際のイベント
            onExecute = MyExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)

            # 入力窓
            inputs: adsk.core.CommandInputs = cmd.commandInputs

            global _selIpt, _tabIpt, _thickness

            _selIpt = inputs.addSelectionInput(
                '_selIpt1',
                'SolidBody',
                'SolidBody'
            )
            # フィルターを設定する
            _selIpt.addSelectionFilter('Bodies')
            # リミットの設定
            _selIpt.setSelectionLimits(1, 1)

            # Create a tab input.
            _thickness = inputs.addValueInput(
                'Thickness',
                'Thickness',
                'mm',
                adsk.core.ValueInput.createByReal(0.0))

            # Connect to the command destroyed event.
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

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

# OKボタンのコントロール
class MyValidateInputsHandler(adsk.core.ValidateInputsEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args: adsk.core.ValidateInputsEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)

        # 1ボディ,厚みとして正の値が入力されているときに実行可能
        global _selIpt, _thickness
        if not ((_selIpt.selectionCount == 1) and ((_thickness.value) > 0)):
            args.areInputsValid = False
            return

# OKボタン選択後
class MyExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.CommandEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)

        # ダイアログ上のSelectionCommandInput
        global _thickness

        product = _app.activeProduct
        design = adsk.fusion.Design.cast(product)
        rootComp = design.rootComponent
        features = rootComp.features

        # シェル前に既存の面情報を取得
        body_info = _app.activeProduct.allComponents[0].bRepBodies[0]
        face_info = body_info.faces

        face_ID_out = []
        face_ID_out_sketch = []
        for i in range(face_info.count):
            face_ID_out.append(face_info[i].tempId)
            # loops.countが1以上のとき,外形線以外にラインが存在している
            # この場合,面情報だけではロフトに失敗するので別の処理を行う
            if face_info[i].loops.count > 1:
                face_ID_out_sketch.append(face_info[i].tempId)

        # シェルの実行
        entities1 = adsk.core.ObjectCollection.create()
        entities1.add(body_info)
        shellFeats = features.shellFeatures
        isTangentChain = False
        shellFeatureInput = shellFeats.createInput(entities1, isTangentChain)
        thickness = adsk.core.ValueInput.createByReal(_thickness.value)
        shellFeatureInput.insideThickness = thickness
        shellFeats.add(shellFeatureInput)

        # シェルにより生成した新規の面情報を取得
        face_ID_in = []
        face_ID_in_sketch = []
        for i in range(face_info.count):
            ID = face_info[i].tempId
            if not ID in face_ID_out:
                face_ID_in.append(ID)
                if face_info[i].loops.count > 1:
                    face_ID_in_sketch.append(face_info[i].tempId)
        
        # 探索方法:シェルにより生成される面に振られるIDと既存のIDには関係性がありそうなのでそれを利用
        for i in range(len(face_ID_out)):
            for j in range(face_info.count):
                if face_ID_out[i] == face_info[j].tempId:
                    # ボディ平面に外形以外のループがある場合,スケッチを作成することで
                    # 外形と穴部分のプロファイルを用意する
                    if face_ID_out[i] in face_ID_out_sketch:
                        sketches = rootComp.sketches
                        sketchPlane = face_info[j]
                        sketch1 = sketches.add(sketchPlane)
                        profile1 = sketch1.profiles.item
                    else:
                        profile1 = face_info[j]
                    n = face_info[j].loops.count
                elif face_ID_in[i] == face_info[j].tempId:
                    if face_ID_out[i] in face_ID_out_sketch:
                        sketches = rootComp.sketches
                        sketchPlane = face_info[j]
                        sketch2 = sketches.add(sketchPlane)
                        profile2 = sketch2.profiles.item
                    else:
                        profile2 = face_info[j]
            create_by_Loft(profile1, profile2, n)

class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.CommandEventArgs):
        adsk.terminate()

 

 

 

@kandennti さん

とんでもないです!

コード拝見して勉強させていただきます.

シェルまでスクリプト内で実行したかった理由は,外側と内側の面を判別したかったからです.

プログラム内では

・シェル前に面のIDを取得

・シェル後,新規で生成された面のIDを取得

することで内外の面を判別しています.

 


kandennti
Mentor
Mentor

@ChaosComb さん

 

自分の分が必死でまだよく見ていないので・・・後で見させてもらいます。

 

>外側と内側の面を判別したかったからです.

あまり知られていないのですが、Componentオブジェクトに検索する為の

メソッドがあります。

・findBRepUsingPoint:中心点と球で探す

・findBRepUsingRay:始点とベクトル(線)で探す

今回はfindBRepUsingRayを使用して相手の面を見つけ出しました。

https://kantoku.hatenablog.com/entry/2022/08/03/130832 

 

又、不完全なのですが一部開放されているような形状でも板状に

分割出来るようにしたものがこちらにあります。

https://kantoku.hatenablog.com/entry/2022/08/03/155705

loverinlen
Advocate
Advocate

@kandennti さん @ChaosComb さん

す、すごいです…

実際にそう言うスクリプトはどうしたら実行できるのでしょうか

作成方法は調べたら出てきたのですが、他者が作ったものなどの実行方法は調べても出てこず…

0 件のいいね

kandennti
Mentor
Mentor

@loverinlen さん

 

こちらの方法で新規のスクリプトを作成し、コードをコピペしてください。

https://kantoku.hatenablog.com/entry/2021/02/15/161734#%E7%B4%A0%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89 

 

保存してから、”スクリプトとアドイン”のメニューから

新規に作成したスクリプトをダブルクリックでスタートします。

loverinlen
Advocate
Advocate

これのDLが必要なのですね?

スクリーンショット 2022-08-03 16.35.05.png

ChaosComb
Advisor
Advisor

@loverinlen さん

 

そうです!


loverinlen
Advocate
Advocate

とりあえずシェル済なので

@kandennti さんのもので挑戦してみたいのですが

 

今すでに書いてあるものを消して

Fusion360API Python script

から下全てをコピペでいいのでしょうか 

 

スクリーンショット 2022-08-03 17.02.52.png

ChaosComb
Advisor
Advisor

それで大丈夫だと思いますよ!

コピペした後に保存して,Fusion360側から起動させてください.