あまり細かな事は書きませんが、こちらに僕がupしたgithubの
リンクがあります。
Fusion 360 Internals: Trying to re-invent Fusion’s GUI (and failing)
昨年、記載された御本人から "あなたも興味があるだろう" と
直接メールを頂いて教わりました。
知識としては覚えています(理解はしていない)が、あまり
興味はありませんでした・・・。
あまり細かな事は書きませんが、こちらに僕がupしたgithubの
リンクがあります。
Fusion 360 Internals: Trying to re-invent Fusion’s GUI (and failing)
昨年、記載された御本人から "あなたも興味があるだろう" と
直接メールを頂いて教わりました。
知識としては覚えています(理解はしていない)が、あまり
興味はありませんでした・・・。
昨年の後半だったかな? Fusion360にコンフィギュレーションと言う機能が追加されました。
デモの状態の時から知っていて "おぉすごい" と思っていたのですが、
使い方を知らず・・・と言いますか、試してもいませんでした。
ざっくりですが、どんな機能か?と言いますと、一つのファイルに複数の
類似したデザインを持たす事が出来る機能です。
詳しくはこちらです。(丸投げ)
Help
それなりにやると、ここを切り替えるだけで形状が切り替わります。
凄いですよね。
最近になり、APIにもコンフィギュレーションの機能が追加されました。
そこで、こちらの質問を見つけました。
Name of the current configuration? - Autodesk Community
アクティブなコンフィギュレーションの名前はどうやって取得するのか?
と言う事の様です。確かにHelpを見ても取得の仕方がわからないです。
(そもそも使ったことの無い機能なので、何もかもわからないです)
で、あちらにレスしましたが、探し回った結果こんな感じでした。
# Fusion360API Python script import traceback import adsk.core as core import adsk.fusion as fusion def run(context): ui: core.UserInterface = None try: app: core.Application = core.Application.get() ui = app.userInterface des: fusion.Design = app.activeProduct if not des.isConfiguredDesign: return configTopTable: fusion.ConfigurationTopTable = des.configurationTopTable actRow: fusion.ConfigurationRow = configTopTable.activeRow ui.messageBox(actRow.name, "Active Configuration Name") except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
何となく、Designオブジェクトにありそうだな・・・とは目星を付けていました。
configurationTopTableプロパティからゴリゴリと って感じですね。
イナーシャが欲しいのですが、ササっと検索してもProductばかり見つかったのですが
だったのですが、サーフェスで欲しいです。
結局、全部入りのサイトがありました。
Measuring Mass and Inertia | CATIA V5 Automation
消えてしまうと困るので、お借りする。
まず単位はこちら。
メートル法っぽいです。
で、
Product(Partも) : GetTechnologicalObject
Body(形状セットも) : SPAWorkbench.Inertias.Add
で、サーフェスは直接の方法が無い様なのですが、新規の形状セットに
突っ込んで、形状セットのイナーシャで大丈夫だよ と
記載されている気がします。
そこで、好みな部分も有ってこんな感じにしてみました。
'イナーシャの取得 '''param:surf-対象サーフェス '''return:array(Inertia, HybridBody) - 失敗:empty private Function get_surf_inertia( _ Byval surf As HybridShape) As variant set get_surf_inertia = empty If surf Is Nothing Then exit function dim pt As Part set pt = get_parent_of_T(surf, "Part") Dim objRef As Reference Set objRef = pt.CreateReferenceFromObject(surf) Dim intType As Integer intType = pt.HybridShapeFactory.GetGeometricalFeatureType(objRef) If intType = 5 Then exit function Dim geoSet As HybridBody Set geoSet = pt.HybridBodies.Add geoSet.Name = "Temp_ForInertiaMeasure" Dim objJoin As HybridShapeAssemble Set objJoin = pt.HybridShapeFactory.AddNewJoin(objRef, objRef) objJoin.RemoveElement 2 pt.UpdateObject objJoin geoSet.AppendHybridShape objJoin On Error Resume Next Dim objSPAWorkbench As Workbench Set objSPAWorkbench = pt.Parent.GetWorkbench("SPAWorkbench") Dim objInertia As Inertia Set objInertia = objSPAWorkbench.Inertias.Add(geoSet) On Error GoTo 0 if objInertia is nothing then exit function get_surf_inertia = array(objInertia, geoSet) End Function '指定した型をParentから取得 - typenameで取得出来るもので '''param:aoj-対象要素 '''param:T-目的の型名 '''return:AnyObject - 失敗:Nothing private Function get_parent_of_T( _ ByVal aoj As AnyObject, _ ByVal T As String) _ As AnyObject Dim aojName As String Dim parentName As String On Error Resume Next Set aoj = as_dispath(aoj) aojName = aoj.name parentName = aoj.Parent.name On Error GoTo 0 If TypeName(aoj) = TypeName(aoj.Parent) And _ aojName = parentName Then Set get_parent_of_T = Nothing Exit Function End If If TypeName(aoj) = T Then Set get_parent_of_T = aoj Else Set get_parent_of_T = get_parent_of_T(aoj.Parent, T) End If End Function 'ディスパッチ '''param:o-対象要素 '''param:T-目的の型名 '''return:CATBaseDispatch Private Function as_dispath( _ byval o As INFITF.CATBaseDispatch) As INFITF.CATBaseDispatch Set as_dispath = o End Function
関数だけなので動かないですし、VSCodeで書いただけで動かしていないので
間違っている可能性が大。
久々にFusion360です。
こちらを答えてみました。
How to create a `BrowserCommandInput` and populate it with initial data from Fusion? - Autodesk Community
"ダイアログを表示する時にBrowserCommandInputに初期値を渡したい”
と言う質問だと受け止めて答えてみました。
BrowserCommandInputはダイアログ上にHTMLを表示させる
コマンドインプットです。
全く使った事が無いのですが、仕組み的には全くパレットと同じです。
コードを見るとCommandCreatedイベント時にやろうとしていますが、
タイミングが違うんですよ。
CommandCreatedイベント時はコマンド自体が出来ておらず、イベント
終了後にコマンドが出来上がります。
その為、初期値の受け渡しの python -> javascript の一連の動きは
pythonでコマンド作成 -> ダイアログ表示 -> javascriptのDOMContentLoadedイベント発火 -> javascript側で"HTML読み込んだよ"と送信 -> pythonのincomingFromHTMLイベント発火 -> pythonで必要な情報を戻り値として送信 -> javascriptで情報を受け取り -> html側に反映
と結構な処理をさせる必要があります。
それともう一つ。BrowserCommandInputで表示させているブラウザは、javascriptは
非同期処理で書かなければなりません。
フォーラムではファイルを添付して中身が見れないので、こちらで
公開しておきます。(こちらはスクリプトです)
まずはエントリーポイントとなる、"BrowserCommandInputInitialDataTest.py"
# Fusion360API Python script import traceback import adsk import adsk.core as core import json _app: core.Application = None _ui: core.UserInterface = None _handlers = [] CMD_INFO = { 'id': 'kantoku_BrowserCommandInputInitialDataTest', 'name': 'test', 'tooltip': 'test' } _browserIpt: core.BrowserCommandInput = None class MyCommandCreatedHandler(core.CommandCreatedEventHandler): def __init__(self): super().__init__() def notify(self, args: core.CommandCreatedEventArgs): try: global _handlers cmd: core.Command = core.Command.cast(args.command) inputs: core.CommandInputs = cmd.commandInputs onDestroy = MyCommandDestroyHandler() cmd.destroy.add(onDestroy) _handlers.append(onDestroy) onHTMLEvent = MyHTMLEventHandler() cmd.incomingFromHTML.add(onHTMLEvent) _handlers.append(onHTMLEvent) global _browserIpt _browserIpt = inputs.addBrowserCommandInput( "_browserIptId", "Active Document Name:", "index.html", 50, ) except: _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class MyHTMLEventHandler(core.HTMLEventHandler): def __init__(self): super().__init__() def notify(self, args): try: htmlArgs = core.HTMLEventArgs.cast(args) app: core.Application = core.Application.get() if htmlArgs.action == 'htmlLoaded': args.returnData = json.dumps({ "name": app.activeDocument.name, }) except: _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class MyCommandDestroyHandler(core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args: core.CommandEventArgs): adsk.terminate() def run(context): try: global _app, _ui _app = core.Application.get() _ui = _app.userInterface cmdDef: core.CommandDefinition = _ui.commandDefinitions.itemById( CMD_INFO['id'] ) if not cmdDef: cmdDef = _ui.commandDefinitions.addButtonDefinition( CMD_INFO['id'], CMD_INFO['name'], CMD_INFO['tooltip'] ) 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()))
続いて、表示されるHTMLファイル(index.html)。
(javascriptもこれに書き込んでます)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div> <label id="activeDocName">Unknown</label> </div> </body> <script> document.addEventListener("DOMContentLoaded", () => { let adskWaiter = setInterval(() => { if (window.adsk) { clearInterval(adskWaiter); adsk.fusionSendData("htmlLoaded", "").then((ret) => { let data = JSON.parse(ret); let activeDocNameLabel = document.getElementById("activeDocName"); activeDocNameLabel.textContent = data["name"]; }, 100); } }); }); </script> </html>
こんなの知らなかった・・・。
'vba Option Explicit Sub commandBar_test() Dim vbe As Object Set vbe = Application.vbe 'CommandBar Dim cBar As Object Set cBar = get_commandBar("Custom1") 'CommandControl Dim myControl For Each myControl In cBar.Controls Call myControl.Delete Next Set myControl = cBar.Controls _ .Add(Type:=msoControlComboBox, before:=1) With myControl .AddItem Text:="First Item", Index:=1 .AddItem Text:="Second Item", Index:=2 .DropDownLines = 3 .DropDownWidth = 75 .ListHeaderCount = 0 End With End Sub 'https://learn.microsoft.com/ja-jp/office/vba/api/office.commandbars Private Function get_commandBar( _ ByVal name As String) As Object Dim vbe As Object Set vbe = Application.vbe On Error Resume Next Dim cmdBar As Object Set cmdBar = vbe.CommandBars.Item(name) On Error GoTo 0 If cmdBar Is Nothing Then Set cmdBar = vbe.CommandBars.Add( _ name:=name, _ Position:=msoBarFloating _ ) End If cmdBar.Visible = True Set get_commandBar = cmdBar End Function
実行するとこんなのが表示されます。
久々にVBAで刺激を感じた・・・可能性が高まる。
こちらの続きです。
VBAのリスト問題4 - C#ATIA
いよいよ先人の方々が作られたものを試す事にします。
githubで見ても、結構な数が公開されていますね。いかに他の方々も
悩まれているというか、不満に思っているかが分かります。
多すぎて、とても全ては試せませんね。
チラチラっと見た感じですが、中身はCollectionのものと配列のもの
がありました。・・・個人的にはCollectionの利用は考えないけど。
で、とりあえずこちらを試す事としました。
GitHub - nanbu/XArray: Array class for VBA
中は配列で、1クラスにまとまってますし、日本人の方だったので
親近感がありました。
もう一つは、過去に試した事があるariawaseです。
GitHub - vbaidiot/ariawase: Ariawase is free library for VBA cowboys.
・・・9年前かな?
一緒に公開されている "vbac.wsf" ばかりが目につきますが、
ariawaseは本当にすごいです。僕の知る限り、VBAのライブラリ
としては桁違いに高水準のものです。
但し、困るのはHelpらしきものが無いので、理解しきれない。
前回のものも含めてこんな感じでテストです。
vba Option Explicit Private sw_ As StopWatch Sub Custom_Array_test() Dim count As Long count = 500000 Set sw_ = New StopWatch 'XArray sw_.start Debug.Print "-- xArray--" Call test_xarray(count) Call sw_.total("Total" & ":") 'ariawase sw_.start Debug.Print "-- ariawase --" Call Ext.ArrRange(0, count) Call sw_.total("Total" & ":") 'preserve一度で sw_.start Debug.Print "-- 一度で--" Call preserve_once(count) Call sw_.total("Total" & ":") sw_.start Debug.Print "-- 毎回 --" Call preserve_every_time(count) Call sw_.total("Total" & ":") End Sub Private Sub test_xarray( _ ByVal count As Long) Dim xArr As Object Set xArr = New XArray Dim i As Long For i = 0 To count - 1 Call xArr.Add(i) Next End Sub Private Sub preserve_once( _ ByVal count As Long) Dim ary() As Long ReDim ary(count) Dim i As Long For i = 0 To count - 1 ary(i) = i Next End Sub Private Sub preserve_every_time( _ ByVal count As Long) Dim ary() As Long Dim i As Long For i = 0 To count - 1 ReDim Preserve ary(i) ary(i) = i Next End Sub
結果はこちら
-- xArray-- Total:7.953125s -- ariawase -- Total:1.421875s -- 一度で-- Total:0.0234375s -- 毎回 -- Total:0.8125s
おっと意外。毎回ReDim Preserveの方が、ariawaseより
速いですね。でも、ariawaseはあの1行だけで済むんですよね。
こちらの続きです。
VBAのリスト問題3 - C#ATIA
進みが悪い・・・。
続いて動的配列です。
Long型だけで比べるとこんな感じです。
**可変長配列** Long 代入:0.0234375s 呼出:0.0234375s **可変長配列 Preserve** Long 代入:0.9375s 呼出:0.0234375s
呼び出しは誤差程度なのですが、代入には差があります。
まぁ当然だとは思います。
代入だけをこの様な感じで再度テストします。
'vba Option Explicit Private sw_ As StopWatch Sub ReDim_Preserve_test() Dim count As Long count = 1000000 '一度で Set sw_ = New StopWatch sw_.start Debug.Print "-- 一度で --" Call preserve_once(count) Call sw_.total("Total" & ":") '毎回 Set sw_ = New StopWatch sw_.start Debug.Print "-- 毎回 --" Call preserve_every_time(count) Call sw_.total("Total" & ":") End Sub Private Sub preserve_every_time( _ ByVal count As Long) Dim ary() As Long Dim i As Long For i = 0 To count - 1 ReDim Preserve ary(i) ary(i) = i If i Mod 100000 = 0 Then Call sw_.split(CStr(i) & ":") End If Next End Sub Private Sub preserve_once( _ ByVal count As Long) Dim ary() As Long ReDim ary(count) Dim i As Long For i = 0 To count - 1 ary(i) = i If i Mod 100000 = 0 Then Call sw_.split(CStr(i) & ":") End If Next End Sub
結果を見やすくするとこんな感じです。
最初に必要なサイズを確保した場合、書き出す意味が無いぐらい
速いですね・・・。
そこが目的では無く、毎回ReDim Preserveしている方は、
後半に向けて徐々に遅くなっています。
これは理由を知ってます。
配列の場合、メモリはコレクションの時と異なり連続した状態で確保されます。
その為、配列要素の1個のサイズが確定すれば、100個先だろうが1万個先だろうが
どこに目的の要素があるかが分かっている為、呼び出しも高速です。
ReDim Preserveが徐々に遅くなる原因は、こんな理由でしょう。
サイズが5個から6個になる場合、6個目は5個目の隣のメモリを確保する
訳ではないです。
まず、6個分のメモリを確保します。これがReDim。
続いて元の5個分の内容をコピーする。これがPreserve。
これを1個サイズを大きくする度に行っているので、
後半になればなるほど、コピーする量が多くなり遅くなる
のだと思っています。
・・・とは言え、コレクションより遥かに速い。