C#ATIA

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

スケッチの点を元にボディを複製したい1

先日のアップデートで、明らかにCustom Featuresの改善に
取り組まれている事をヒシヒシと感じているので、しんどいのですが
再挑戦しようかと考えています。

・・・こちらに嫌味を書いたので後ろめたい思いも有ってです。
Re: Custom Feature API Preview - Feedback Thread - Autodesk Community



Custom Features目的で作り始めた2回転メビウスの帯での再挑戦は
止めておきます。
GitHub - kantoku-code/Fusion360_Two-turn_Mobius_strip: Create a two-turn Möbius strip.
あれはあまりに処理が重すぎます。


そこで、以前からCATIAに比べて不足していると感じている、
ユーザーパターンを取り組みたいと思っています。(結構前から・・・)

かなり以前に小原さんもアイデアに書いています。
スケッチ点に形状をパターンコピーできるようにしてほしい - Autodesk Community


いきなりCustom Featuresのアドインを作るのは、辛すぎるので
機能する部分から作っていくことにします。


この様なデータを用意しました。
f:id:kandennti:20210708190553p:plain

円柱のボディと点を配置したスケッチです。
但しスケッチはボディとはZ方向的には一致していない状態です。
(言葉での表現が難しい)
CATIAのユーザーパターンは、参照したスケッチ上に出来るのではなく
点のXY座標を移動させる様に配置します。
(ここも言葉では難しい)

要はCATIAのユーザーパターンを視野に入れて作りたいのです。
で、スクリプトを作ってみました。

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

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

        # msg :str = 'Select Body'
        # selFiltter :str = 'Bodies'
        # sel :adsk.core.Selection = selectEnt(msg ,selFiltter)
        # if not sel: return
        # body :adsk.fusion.BRepBody = sel.entity

        # msg = 'Select Sketch'
        # selFiltter :str = 'Sketches'
        # sel :adsk.core.Selection = selectEnt(msg ,selFiltter)
        # if not sel: return
        # skt :adsk.fusion.Sketch = sel.entity

        execUserPattern(
            root.bRepBodies[0],
            root.sketches[0])

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


# 将来的に body skt は同一コンポーネントの制限を行いたい
def execUserPattern(
    body :adsk.fusion.BRepBody,
    skt :adsk.fusion.Sketch):

    # body cog
    VERYHI = adsk.fusion.CalculationAccuracy.VeryHighCalculationAccuracy
    prop :adsk.fusion.PhysicalProperties = body.getPhysicalProperties(VERYHI)
    bodyCenter :adsk.core.Point3D = prop.centerOfMass

    # get points
    origin :adsk.fusion.SketchPoint = skt.originPoint
    p :adsk.fusion.SketchPoint
    pnts = [p.worldGeometry for p in skt.sketchPoints
        if p != origin]

    # get sketch axis
    _, _, _, zAxis = skt.transform.getAsCoordinateSystem()

    tmpMgr :adsk.fusion.TemporaryBRepManager = adsk.fusion.TemporaryBRepManager.get()
    cloneMat :adsk.core.Matrix3D = adsk.core.Matrix3D.create()
    clones = []
    for p in pnts:
        plane :adsk.core.Plane = adsk.core.Plane.create(
            bodyCenter, zAxis)

        infinite :adsk.core.InfiniteLine3D = adsk.core.InfiniteLine3D.create(
            p, zAxis)

        targetPnt :adsk.core.Point3D = plane.intersectWithLine(infinite)

        vec :adsk.core.Vector3D = bodyCenter.vectorTo(targetPnt)
        cloneMat = adsk.core.Matrix3D.create()
        cloneMat.translation = vec

        clone :adsk.fusion.BRepBody = tmpMgr.copy(body)
        tmpMgr.transform(clone, cloneMat)
        clones.append(clone)

    comp :adsk.fusion.Component = body.parentComponent
    bodies :adsk.fusion.BRepBodies = comp.bRepBodies
    baseFeat : adsk.fusion.BaseFeature = comp.features.baseFeatures.add()
    baseFeat.startEdit()
    for clone in clones:
        bodies.add(clone, baseFeat)
    baseFeat.finishEdit()


def selectEnt(
        msg :str, 
        filtterStr :str) -> adsk.core.Selection :

    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        sel = ui.selectEntity(msg, filtterStr)
        return sel
    except:
        return None

余計なコードが色々あるのですが、時間が無いのでそのまま。
実行結果はこんな感じです。
f:id:kandennti:20210708191359p:plain

スケッチの点上では無く、水平に移動して配置される感じです。
Point3DやPlaneのオブジェクトに射影処理のようなものが無いんですよね。
自分が気が付いていない可能性もあるのですが。
結果的に少し遠回りな処理になっちゃってます。

又、移動させるベクトルをボディの重心上に平面を作ってゴニョゴニョ
処理したのですが、よく考えたら逆ですね。
スケッチ平面上に作らないと、後々の処理に間違いなく悪影響が出そう。

出だしから、直すべき部分が多すぎる。

Fusion360 Ver2.0.10440

オンラインドキュメントの方がフライングアップデートだった
のですが、ソフト自体のアップデートが来ました。

主要なものはよそにお任せして・・・
テキストコマンドは小さなアップデートでも毎回リストを取得して
変更を確認しています。

気になるのがこちら
f:id:kandennti:20210707124244p:plain

Fusion.Replace                                   - [/Off|/On|/All] Insert XRef command instead does replace on selected XRef instance.

個人ライセンスでは、ほぼ無縁ですが外部ドキュメントを
コンポーネントとして取り込んでいる場合、差し替え出来るように
なるんじゃないのかな? あれ今でも出来るのかな?
(XRefはインプレイス編集絡みです)

APIはこれかな
Fusion 360 Help
前からフォーラムで "次のアップデートで入れるから" とアナウンス
されていたやつです。

デバッグ時に

print(hoge)

だと、Fusion360の画面とVSCodeを画面切り替えながら行わなきゃ
いけないので、面倒だったので

def dumpMsg(msg :str):
    adsk.core.Application.get().userInterface.palettes.itemById('TextCommands').writeText(str(msg))

こんな感じの関数用意して、テキストコマンドのパレットに垂れ流していました。
フォーラムのサンプルにも散々付けて公開していたので、
便利さにAutodeskさんも気が付いてくれたみたいで
実装してくれたのだと思います。(勝手な妄想)

getNumberOfSections

こちらに取り組みました。
解決済み: 工具径補正が使われて場合 ポスト処理後の工具詳細情報に追加させたい - Autodesk Community

ポストコンフィグの改造は、自分にも刺激的です。忘れないうちに覚書を。


ポスト処理する際、こちらのエントリー関数なるものと戦う事になります。
Entry Functions


かなり大雑把な流れとしてはこんな感じです。

・onOpen()
 ↓
・onSection()
 ↓  ↑
・onSectionEnd()
 ↓
・onClose()

あぁ上手く表現出来ない。onSection - onSectionEnd 間はツールパス分呼び出されます。
最初は簡単かな? と思っていたのですが、工具径補正(G41,G42)は工具が情報を
持っているのではなく、ツールパスが持っています。

そしてご希望の工具情報は onOpen で処理されています。
つまり onOpen で工具情報を書き出そうとしても、onSection が未処理の為
工具径補正を使用しているのか? いないのか? が分らぬ状態のタイミングで
工具情報を書き出す事となり、
”少し試した所そうでもない事が分かりました。”
と言う運びとなったわけです。
要は onOpen で工具径補正の使用の有無を知る方法を見付けなければなりません。

実はちょっと心当たりがありました。
セットアップシート用のスクリプトを作ろうとした際(完全に中断・・と言うか忘れている)
何かの拍子で onOpen で全ての Section を走査する関数か何かがあり
”あぁ onSection 使わなくても出来るじゃん”
と思った事があったのですが、肝心の関数名を忘れてしまい、出だしから苦労しました。
APIでのツールパスはOperationオブジェクトなので、勘違いして余計に時間がかかりました)

で、やっと見つけたのが "getNumberOfSections" 関数です。

function getCompensationInfo() {
  strategyWhiteLst = ["contour2d", "bore", "thread", "circular"];
  compensationTypeBlackLst = ["computer", "off"];
  g41Lst = ["climb-control", "climb-wear", "conventional-inverseWear", "left-control", "left-wear", "right-inverseWear"];
  g42Lst = ["conventional-control", "conventional-wear", "climb-inverseWear", "right-control", "right-wear", "left-inverseWear"];

  var dict = {}
  for (var i = 0; i < getNumberOfSections(); ++i) { // <-これです
    var sectioni = getSection(i);
・・・

getNumberOfSections関数は、ポスト処理に投げられたツールパス達を投げ返してくれます。
つまりonSection関数が呼び出される前でも、ツールパスの情報が取得出来ます。

ループで回しまくっている section(ツールパス) からは、大半は Parameter として
情報を引き出せるので、必要な情報を引っ張り出します。

section.getParameter(<パラメータ名>)

肝心のパラメータ名は、Dumperポストで処理させて地道に探しています。
https://cam.autodesk.com/hsmposts?p=dump

これらの方法で習得した工具径補正情報は、

Key:Tコード Value:G41 か G42 又は 両方

の辞書(Javascrptだと、ハッシュテーブルかな?)を作成し、工具情報を書き出す際、
KeyとしてのTコードを利用し情報を引き出して書き出しました。

絶対、言葉の説明だけじゃ理解されないと思う。(=他人には役に立たない)

空中にツールパスを作る

かなり以前、フォーラムにしたのですが・・・
製造のプローブでスケッチラインに沿った動き - Autodesk Community
回答無しですね。恐らく方法ないんでしょう。

仕方が無いので、別の方法で。


モデルを無視して、ラインに沿った状態で空中にツールパスを
作りたいんです。
f:id:kandennti:20210702182009p:plain
こんな感じで往復させたいんです。で、出来ました。

トレースの "パスの繰り返し" "両方向" をONでした。
f:id:kandennti:20210702182406p:plain

ガイドになるラインの指定ですが、矢印の向きがボディ側に
しておかないと空中から開始されません。
f:id:kandennti:20210702182716p:plain
感覚が逆なのですが、Space-eもそうでした。
処理的な関係で、何かあるんだろうなぁ。


続いて、ライン2本指定します。
f:id:kandennti:20210702183249p:plain
1本目を一方だけ動いて、2本目も一方だけ動き、
1本目の残りの動きをして・・・言葉じゃ表現できないのですが、
早い話望んだ動きが作れなかったです。
頑張ったんですけどね。


もう一つ、円形パターン。
f:id:kandennti:20210702183559p:plain
最初のものをパターンしました。こちらは問題なさそうです。


結論。行けそう。
取り組みの為のモチベーションが上がりました。
念のためですが、加工じゃなくて三次元測定時のプログラムを
CAMソフトで作ろう、と言う試みです。
"Fusion360CAMに検査の機能有るじゃない" と思われるかも
知れませんが、あちらは主にマシニングセンタ等の工作機上での
お話でして、三次元測定機のプログラムとは全く異なるんです。
うちの三次元測定機は!

NXのトライアル版

知りませんでしたよ、NXのトライアル版があるなんて

NXを無料で使う方法とは?NXの使い方を徹底解説! | キャド研

NXって触ったこと無いんですよ、画面を見たことも無い。
トライアル版の制限は何だろう?
期間はあるのかな?

亀2

こちらの続きです。
亀1 - C#ATIA

早速作ってみました。

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

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

        PNT3D = adsk.core.Point3D
        pnts = []
        a = 2
        for i in range(0, 100):
            theta = math.radians(unitDeg) * i
            r = a * theta
            x = r * math.cos(theta)
            y = r * math.sin(theta)

            pnts.append(PNT3D.create(x,y,0))

        skt = root.sketches.add(root.xYConstructionPlane)
        skt.arePointsShown = False

        drawPolygonSpirals(pnts, skt)

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

def drawPolygonSpirals(
    pnts :list,
    skt :adsk.fusion.Sketch):

    skt.isComputeDeferred = True
    lines : adsk.fusion.SketchLines = skt.sketchCurves.sketchLines

    for p1, p2 in zip(pnts,pnts[1:]):
        lines.addByTwoPoints(p1, p2) 

    skt.isComputeDeferred = False

実行結果はこちら!
f:id:kandennti:20210630123918p:plain

色々足りないのですが、まぁ出だしは良いでしょう。
・・・業務が忙しいと、別の事をしたくなる性格何とかならないかなぁ。

亀1

こちら、ちょっと面白そうなんですが。
Polygon spirals - Autodesk Community

"turtle" と言う標準ライブラリがあるなんて知りませんでした。
試したところ、確かにFusion360Pythonでも動きました。
・・・クッソ遅い。

もっと早く動かないのかな?と思い少し調べましたが、ちょっとだけ
速くなりましたが、それでも遅い。

そもそも描写して欲しいわけでは無く、単純に座標値が欲しいだけなので
"turtle" を使わなくても良いはず。

調べた結果、アルキメデスの螺旋を利用し該当する角度の座標値だけ
取得出来れば良いだけでしょう。色々調整の為の計算が必要ですが。