C#ATIA

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

無限直線と面の交差した座標の取得2

こちらの続きです。
無限直線と面の交差した座標の取得 - C#ATIA

まず、こんなボディを作ります。底面の中心が原点です。
f:id:kandennti:20180528190203p:plain

前回のコードを修正しました。

#FusionAPI_python
#Author-kantoku
#Description-無限直線と選択面の交差テスト

import adsk.core, adsk.fusion, traceback
_app = adsk.core.Application.get()
_ui = _app.userInterface

def run(context):
    try:
        #面の選択
        selFilters = 'Faces'
        sel = _ui.selectEntity('面を選択/ESC-中止', selFilters)
        if sel is None: return
        brep = sel.entity
        surf = adsk.core.Surface.cast(brep.geometry)
        
        #交点座標
        GetIntersectPosition(surf, [0,0,0], [0,0,10.0])
        
        GetIntersectPosition(surf, [500,0,0], [500,0,10.0])
        
        GetIntersectPosition(surf, [0,0,0], [0,500,0])
        
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def GetIntersectPosition(plane, start, end):
    try:
        #直線
        st_pnt = adsk.core.Point3D.create(start[0]*0.1, start[1]*0.1, start[2]*0.1)
        ed_pnt = adsk.core.Point3D.create(end[0]*0.1, end[1]*0.1, end[2]*0.1)
        line = adsk.core.Line3D.create(st_pnt, ed_pnt).asInfiniteLine()
        
        lst = line.intersectWithSurface(plane)
        if lst.count > 0 :
            pnt3d = lst[0]
            msg = 'x:{:.3f}  y:{:.3f}  z:{:.3f}'.format(pnt3d.x*10, pnt3d.y*10, pnt3d.z*10)           
        else:
             msg = '交差しません'
        _ui.messageBox(msg)
        
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

本来、単位に関してはunitsManagerで行うのがFusion360APIのお作法ですが
そこが重要なのではないのでムリムリにやってます。

スプリクト実行後、選択した面と
1)[0,0,0], [0,0,10.0]
2)[500,0,0], [500,0,10.0]
3)[0,0,0], [0,500,0]
をそれぞれ通過する無限直線との座標値を表示させます。

今回は、ボディの天面を選択してみます。
結果としては、
f:id:kandennti:20180528190217p:plain
左から、1)2)3)です。
1)は正しい値です。3)も交差しないので正しいです。
問題は2)なんです。天面とはかけ離れた位置を表示します。
原因は単純で、面側の情報が無限平面となってしまっているからです。

        brep = sel.entity #←選択した面のエンティティを取得(BRepFace型)
        surf = adsk.core.Surface.cast(brep.geometry)#←BRepFaceのジオメトリを取得

surfをキャストしてもしなくても、Plane型でした。
f:id:kandennti:20180528190228p:plain
名称からしていかにも無限平面っぽいです。

これぐらい単純な面と線であれば、何かしら計算すれば境界内に入っているか?
どうか? を判断できますが、傾いていた場合は・・・と言うところで
悩んでます。
点がサーフェス上に存在しているかどうかを判断出来れば良いのですが・・・。

無限直線と面の交差した座標の取得

タイトルが異なりますが、こちらの続きです。
マウスカーソルの座標値を取得する2 - C#ATIA
不人気ですが、こちら諦めていないです。

自宅で細々と(半分寝ながら)作っていた時は上手くいかなかったの
ですが、何故か会社では上手くいきました。

#FusionAPI_python
#Author-kantoku
#Description-無限直線と面の交差テスト

import adsk.core, adsk.fusion, traceback

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        
        #水平面
        pnt_ori = adsk.core.Point3D.create(0, 0, 0)
        vec_h = adsk.core.Vector3D.create(0, 0, 1)
        surf_h = adsk.core.Plane.create(pnt_ori, vec_h)        
        
        GetIntersectPosition(surf_h, [0,0,-10.0], [0,0,10.0])
        GetIntersectPosition(surf_h, [5,0,-10.0], [5,0,10.0])
        
        #垂直面
        vec_v = adsk.core.Vector3D.create(1, 0, 0)
        surf_v = adsk.core.Plane.create(pnt_ori, vec_v)           
        
        GetIntersectPosition(surf_v, [0,0,-10.0], [0,0,10.0])
        GetIntersectPosition(surf_v, [5,0,-10.0], [5,0,10.0])
        
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def GetIntersectPosition(plane, start, end):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        
        #直線
        st_pnt = adsk.core.Point3D.create(start[0], start[1], start[2])
        ed_pnt = adsk.core.Point3D.create(end[0], end[1], end[2])
        line = adsk.core.Line3D.create(st_pnt, ed_pnt).asInfiniteLine()
        
        lst = line.intersectWithSurface(plane)
        if lst.count > 0 :
            pnt3d = lst[0]
            msg = 'x:{:.3f}  y:{:.3f}  z:{:.3f}'.format(pnt3d.x, pnt3d.y, pnt3d.z)
            ui.messageBox(msg)
        else:
            ui.messageBox('交差しません')
        
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

単位はCmなので役にたたないです。

デフォルト [コンポーネント/残り代] と異なる設定のツールパス名を取得2

こちらの続きです。
デフォルト [コンポーネント/残り代] と異なる設定のツールパス名を取得 - C#ATIA

前回はコンポーネント数は未チェックでしたが、チェックするように修正し
実際に使えそうなレベルにして見ました。

//pm2018 macro
//Find_don't_match_ToolPath.mac ver0.0.2
//デフォルト [コンポーネント/残り代] と異なる設定のツールパス名取得
//---------------------------------------------------------------------------------------------------
//ver0.0.1-完成
//ver0.0.2-コンポーネントチェック追加
//---------------------------------------------------------------------------------------------------

function main() {
	bool debug = 0 //debug mode
	
	//check
	call CanExec()
	
	//start
	call Sw_Start()
	graphics lock

	INFOBOX NEW "***** Toolpath ThicknessSetValues info *****"
	INFOBOX CLEAR FORMAT
	
	//defalt ThicknessSetValues
	call PushInfobox('Getting Surface Defaults Info.' + crlf)
	string list def_tvInfo = {}
	string def_cmp_count = ''
	call Get_defalt_ThicknessSetValuesInfo($def_tvInfo, $def_cmp_count)
	
	//filter
	call PushInfobox('Getting Toolpath Surface Parameter.' + crlf)
	string filter = ''
	call Initfilter($def_tvInfo, $filter)
	
	//pram match toolpath
	entity list tps = folder('toolpath')
	$tps =  filter($tps , $filter)
	
	//components
	call PushInfobox('Getting Toolpath Surface Components')
	string list cmps = {}
	call GetCompsCount($tps, $cmps)
	
	//&components match toolpath
	call GetCompMatchList($tps, $cmps, $def_cmp_count, $tps)
	
	//doesn't match toolpath
	call PushInfobox("Getting doesn't match Toolpath." + crlf + crlf)
	string list all_tps =  extract(folder('toolpath'), 'name') 
	string list match_tps =  extract($tps, 'name') 
	string list unmatch_tps =  subtract($all_tps, $match_tps)
	
	call PushInfobox('デフォルト [コンポーネント/残り代] と異なる設定のツールパスは以下のものです' + crlf)
	//call PushInfobox("It's toolpath that don't match the 'Surface Defaults' values." + crlf)
	call PushInfobox(join($unmatch_tps, crlf))
	
	//finish
	if $debug == 1 {
		string tm = ''
		call Sw_GetTime($tm)
		call PushInfobox(crlf + $tm)
	}
	message info  'Done'
}

//start check
function CanExec() {
	string list msg = {}
	int dmy = 0
	//toolpath
	if size(folder('toolpath')) <1 {
		 $dmy = add_last($msg, 'There is no toolpath to check.')
	}
	if size(folder('model')) <1 {
		 $dmy = add_last($msg, 'The model has not been imported.')
	}
	if not is_empty($msg) {
		message warn join($msg, crlf)
		macro abort
	}
}

//Match components
function GetCompMatchList(entity list tps, string list cmps, 
					string def_cmp, output entity list out) {
	int list rng = {}
	call GetRangeLst(0, size($tps), $rng)
	$rng = reverse($rng)
	bool dmy = 0
	foreach idx in $rng {
		if $cmps == $def_cmp {
			$dmy = remove($tps, $idx)
		}
	}
	$out = $tps
}

//Toolpath components
function GetToolpathCompsCount(entity tp, output string out) {
	int list tv_rng = {}
	call GetRangeLst(0, size($tp.ThicknessSetValues), $tv_rng)
	
	int list cmps = {}
	int dmy = 0
	int surf_cnt = -1
	
	foreach idx in $tv_rng {
		$surf_cnt = -1
		object tv =  $tp.ThicknessSetValues[$idx]
		DELETE SELECTION
		EDIT THICKNESS DEFAULTLIST UPDATE
		\r
		$idx  NEW
		\r
		EDIT THICKNESS SELECT
		call GetSelSurfCount($surf_cnt)
		$dmy = add_last($cmps, $surf_cnt)
	}
	$out = join($cmps, ',')
}

//toolpath_list components
function GetCompsCount(entity list tps, output string list out) {
	$out = {}
	string cmp = ''
	int dmy = 0
	foreach tp in tps {	
		call GetToolpathCompsCount($tp, $cmp)
		$dmy = add_last($out, $cmp)
		call PushInfobox(' .' )
	}
	call PushInfobox(crlf)
}

//select surface count
function GetSelSurfCount(output int num) {
	string $TraceFilePath = macro_path(false) + "\selsurf.txt"
	string list txts = {}
	ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT
	TRACEFILE OPEN $TraceFilePath
	PRINT SELSURFACE
	TRACEFILE CLOSE
	ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
	
	FILE OPEN $TraceFilePath FOR READ AS Input
	FILE READ $txts FROM Input
	FILE CLOSE Input
	DELETE FILE $TraceFilePath

	$num = size($txts) - 1
}

//filter
function Initfilter(string list tvs, output string out) {
	string list prms = {'Mode', 'UseAxialThickness', 'Thickness', 'AxialThickness'}
	int list prm_rng = {}
	call GetRangeLst(0, size($prms), $prm_rng)
	
	int list tv_rng = {}
	call GetRangeLst(0, size($tvs), $tv_rng)
	
	$out = ''
	string list tmp = {}
	int dmy = 0
	string value = ''
	foreach tv_idx in $tv_rng {
		string list info = tokens($tvs[$tv_idx], ',')
		foreach prm_idx in $prm_rng {
			if $prm_idx == 0 {
				$value = '"' + $info[$prm_idx] + '"'
			} else {
				$value = $info[$prm_idx]
			}
			$dmy = add_last($tmp, 'ThicknessSetValues[' + string($tv_idx) + '].' + $prms[$prm_idx] + ' == ' + $value)
		}
	}
	$out = join($tmp, ' and ')
	print = $out
}

//defalt ThicknessSetValues info
function Get_defalt_ThicknessSetValuesInfo(output string list out, output string cmpcnt) {
	string dmy_tp = ''
	call InitDmyTP($dmy_tp)
	entity tp = entity('toolpath', $dmy_tp)
	
	FORM THICKNESS EDIT THICKNESS TAB DEFAULT
	EDIT THICKNESS TAB COMPONENTS TOOLPATH	
	EDIT TOOLPATH ; THICKNESS CLONE_DEFAULT
	THICKNESS ACCEPT
	object list def_tvs = $tp.ThicknessSetValues
	call GetThickSetValInfo($def_tvs, $out)
	call GetToolpathCompsCount($tp, $cmpcnt)
	DELETE TOOLPATH  $dmy_tp	
}

//ThicknessSetValues info
function GetThickSetValInfo(object list tvs, output string list out) {
	int list rng = {}
	call GetRangeLst(0, size($tvs), $rng)
	string info =''
	string list lst = {}
	int dmy = 0
	
	foreach idx in $rng {
		object tv =  $tvs[$idx]
		$info = $tv.Mode + ',' + $tv.UseAxialThickness + ',' + $tv.Thickness + ',' + $tv.AxialThickness
		$dmy = add_last($lst, $info)
	}
	$out = $lst
}	

//dammy toolpath
function InitDmyTP(output string out) {
	$out = new_entity_name('toolpath')
	IMPORT TEMPLATE ENTITY TOOLPATH TMPLTSELECTORGUI 'Finishing/Constant-Z-Finishing.002.ptf'
	FORM CANCEL STRATEGYSELECTOR\nFORM TOOLPATHIMPORT
	EDIT TOOLPATH $out REAPPLYFROMGUI
	YES
	FORM ACCEPT SFConstZFinishing
}

//range
function GetRangeLst(int start, int count, output int list lst) {
	int num = 0
	$lst = {}
	do {
		int dmy = add_last($lst, $start + $num)
		$num = $num + 1
	} while $num < $count
}

//infobox
function PushInfobox(string msg) {
	INFOBOX STYLE "NORMAL"
	INFOBOX APPEND  $msg
}

//clock on
function Sw_Start() {
	CLOCK RESET QUIT
	CLOCK ON QUIT	
}

//clock off
function Sw_GetTime(output string out) {
	string $TraceFilePath = macro_path(false) + "\clock.txt"
	string list txts = {}
	ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT
	TRACEFILE OPEN $TraceFilePath
	CLOCK OFF QUIT
	CLOCK PRINT QUIT
	TRACEFILE CLOSE
	ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
	
	FILE OPEN $TraceFilePath FOR READ AS Input
	FILE READ $txts FROM Input
	FILE CLOSE Input
	DELETE FILE $TraceFilePath

	$out = $txts[0]
}

コメントが英語な部分は、そのうちフォーラムで良い方法が無いものか? を
質問しようと思っているためです。(コード内の英語を含め、自信なし)

実際に業務で使用したデータでチェックしてみました。
前回のマクロでは
f:id:kandennti:20180528151318p:plain
7秒ほど。

今回のマクロでは
f:id:kandennti:20180528151359p:plain
80秒ほど・・・・ガッカリ。
手動でチェックするのを考えれば、確かに速いけど、
マクロで1分以上はかなり苦痛。

デフォルト [コンポーネント/残り代] と異なる設定のツールパス名を取得

こちらの続きです。
ツールパスのコンポーネント残り代情報 - C#ATIA

テスト的なコードとは言え、あまりの処理の遅さのため改善します。
根本的な目的は、全ての情報の取得ではなく任意の設定となっていない
ツールパスさえわかれば良いのです。(修正は手動でチマチマ)

//pm2018 macro
//Get_ThicknessSetValues_Unmatch_ToolPath_Name.mac ver0.0.1
//デフォルト [コンポーネント/残り代] と異なる設定のツールパス名取得

function main() {
	call Sw_Start()
	graphics lock

	//defalt ThicknessSetValues
	string list def_tvInfo = {}
	call Get_defalt_ThicknessSetValuesInfo($def_tvInfo)
	
	//filter
	string filter = ''
	call Initfilter($def_tvInfo, $filter)
	
	//match toolpath
	entity list tps = folder('toolpath')
	$tps =  filter($tps , $filter)
	
	//un_match toolpath
	string list all_tps =  extract(folder('toolpath'), 'name') 
	string list match_tps =  extract($tps, 'name') 
	string list unmatch_tps =  subtract($all_tps, $match_tps)
	
	INFOBOX NEW "***** toolpath ThicknessSetValues info *****"
	INFOBOX CLEAR FORMAT
	call ShowInfobox('デフォルト [コンポーネント/残り代] と異なる設定のツールパスは以下のものです' + crlf)
	call ShowInfobox(join($unmatch_tps, crlf))
	
	//終了
	call Sw_GetTime()
}

//filter
function Initfilter(string list tvs, output string out) {
	string list prms = {'Mode', 'UseAxialThickness', 'Thickness', 'AxialThickness'}
	int list prm_rng = {}
	call GetRangeLst(0, size($prms), $prm_rng)
	
	int list tv_rng = {}
	call GetRangeLst(0, size($tvs), $tv_rng)
	
	$out = ''
	string list tmp = {}
	int dmy = 0
	string value = ''
	foreach tv_idx in $tv_rng {
		string list info = tokens($tvs[$tv_idx], ',')
		foreach prm_idx in $prm_rng {
			if $prm_idx == 0 {
				$value = '"' + $info[$prm_idx] + '"'
			} else {
				$value = $info[$prm_idx]
			}
			$dmy = add_last($tmp, 'ThicknessSetValues[' + string($tv_idx) + '].' + $prms[$prm_idx] + ' == ' + $value)
		}
	}
	$out = join($tmp, ' and ')
	print = $out
}

//defalt ThicknessSetValues info
function Get_defalt_ThicknessSetValuesInfo(output string list out) {
	string dmy_tp = ''
	call InitDmyTP($dmy_tp)
	
	FORM THICKNESS EDIT THICKNESS TAB DEFAULT
	EDIT THICKNESS TAB COMPONENTS TOOLPATH	
	EDIT TOOLPATH ; THICKNESS CLONE_DEFAULT
	THICKNESS ACCEPT
	object list def_tvs = entity('toolpath', $dmy_tp).ThicknessSetValues
	call GetThickSetValInfo($def_tvs, $out)
	DELETE TOOLPATH  $dmy_tp	
}

//ThicknessSetValues info
function GetThickSetValInfo(object list tvs, output string list out) {
	int list rng = {}
	call GetRangeLst(0, size($tvs), $rng)
	string info =''
	string list lst = {}
	int dmy = 0
	
	foreach idx in $rng {
		object tv =  $tvs[$idx]
		$info = $tv.Mode + ',' + $tv.UseAxialThickness + ',' + $tv.Thickness + ',' + $tv.AxialThickness
		$dmy = add_last($lst, $info)
	}
	$out = $lst
}	

//ダミーツールパス
function InitDmyTP(output string out) {
	$out = new_entity_name('toolpath')
	IMPORT TEMPLATE ENTITY TOOLPATH TMPLTSELECTORGUI 'Finishing/Constant-Z-Finishing.002.ptf'
	FORM CANCEL STRATEGYSELECTOR\nFORM TOOLPATHIMPORT
	EDIT TOOLPATH $out REAPPLYFROMGUI
	YES
	FORM ACCEPT SFConstZFinishing
}

//範囲
function GetRangeLst(int start, int count, output int list lst) {
	int num = 0
	$lst = {}
	do {
		int dmy = add_last($lst, $start + $num)
		$num = $num + 1
	} while $num < $count
}

//情報
function ShowInfobox(string msg) {
	INFOBOX STYLE "NORMAL"
	INFOBOX APPEND  $msg
}

//測定スタート
function Sw_Start() {
	CLOCK RESET QUIT
	CLOCK ON QUIT	
}

//時間表示
function Sw_GetTime() {
	string $TraceFilePath = macro_path(false) + "\clock.txt"
	string list txts = {}
	ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT
	TRACEFILE OPEN $TraceFilePath
	CLOCK OFF QUIT
	CLOCK PRINT QUIT
	TRACEFILE CLOSE
	ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
	
	FILE OPEN $TraceFilePath FOR READ AS Input
	FILE READ $txts FROM Input
	FILE CLOSE Input
	DELETE FILE $TraceFilePath

	message info $txts[0]
}

全ツールパスに対してフィルター関数で抜き出すのですが、フィルターの条件もForEachで作り出します。
PMillマクロのString型に文字数制限は無い?(ひょっとしたらあるのかな?)様で、
かなり巨大な文字列を渡しています。

比較対象はデフォルト [コンポーネント/残り代] の設定です。
f:id:kandennti:20180525194927p:plain

実際にテストした結果はこちら
f:id:kandennti:20180525194936p:plain

20本程のツールパスに対して、6秒弱! 正直、驚きました。
前回のものでは同様のデータで3分以上かかっていたので。
但し、前回紹介したコンポーネント数のチェックはしてません・・・。

フィルター関数使おう、ループは遅い。
(コンポーネント数をパラメータで取得したい)

PMillのマクロ時間測定

以前こちらに書いた中に関数のコードだけ載せていたのですが
ツールパスバッチ処理 時間測定 - C#ATIA
エコーコマンド部分に表示される為、ちょっと使いにくかったです。

その為「Sw_GetTime」だけ書き換えました。

function Sw_Start() {
	CLOCK RESET QUIT
	CLOCK ON QUIT	
}

function Sw_GetTime() {
	string $TraceFilePath = macro_path(false) + "\clock.txt"
	string list txts = {}
	ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT
	TRACEFILE OPEN $TraceFilePath
	CLOCK OFF QUIT
	CLOCK PRINT QUIT
	TRACEFILE CLOSE
	ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
	
	FILE OPEN $TraceFilePath FOR READ AS Input
	FILE READ $txts FROM Input
	FILE CLOSE Input
	DELETE FILE $TraceFilePath

	message info $txts[0]
}

これを利用すると、こんな風にダイアログで表示します。
f:id:kandennti:20180525172725p:plain
TRACEFILE OPEN ~ CLOSE は、コマンドエコーに出力されるものを
ファイルで取得出来るんですね。(PRINT PAR ~等)

ツールパスのコンポーネント残り代情報

PowerMillの機能に、コンポーネント残り代と言う機能があるのですが
一般的に使われるものかどうか?
個人的には電極の仕上代を設定する為、必ず使っています。

ところがここの設定の確認が非常にやりにくい為、マクロでチェックしようと
考えているのですが苦戦してます。
f:id:kandennti:20180524190219p:plain
殆どの情報は取得できるのですが、矢印のコンポーネント数を取得する
パラメータが存在しないようで困ってしまいました。

サポートさんに問い合わせても出来ないとの回答は頂いたのですが、
ちょっとだけ気が付いた事がありました。
各セットを選択した状態で、こちらの名称不明のボタンを押すと
コンポーネントとして登録された面を選択した状態になります。
f:id:kandennti:20180524190802p:plain
これを利用すれば、選択面の数を取得する事でコンポーネント数の
取得となりそうです。

//pm2018 macro
//全ツールパスのコンポーネント/残り代情報を垂れ流す

function main() {
	int list rng = {}
	call GetRangeLst(0, 16, $rng)
	
	INFOBOX NEW "***** toolpath ThicknessSetValues info *****"
	INFOBOX CLEAR FORMAT
	
	string info = ''	
	int surf_cnt = 0
	
	FORM THICKNESS EDIT THICKNESS TAB DEFAULT
	EDIT THICKNESS TAB COMPONENTS TOOLPATH
	THICKNESS ACCEPT

	dialogs error off
	dialogs message off
	
	foreach tp in folder('toolpath') {
		string full = pathname('toolpath', $tp)
		EXPLORER SELECT Toolpath $full  NEW
		$info = '-- toolpath : ' + $tp.name + ' --'+ crlf
		object list tvs = $tp.ThicknessSetValues
		foreach idx in $rng {
			object tv =  $tp.ThicknessSetValues[$idx]
			
			$info = $info + string($idx) + ' : '
			$info = $info + $tv.Mode + ' : '
			$info = $info + $tv.UseAxialThickness + ' : '
			$info = $info + string($tv.Thickness) + ' : '
			$info = $info + string($tv.AxialThickness) + ' : '
			
			//コンポーネント数の取得
			graphics lock
			DELETE SELECTION
			EDIT THICKNESS DEFAULTLIST UPDATE
			\r
			$idx  NEW
			\r
			EDIT THICKNESS SELECT
			graphics unlock
			call GetSelSurfCount($surf_cnt)
			$info = $info + string($surf_cnt) + crlf
		}
		$info = $info + crlf
		dialogs message on
		call ShowInfobox($info)
		dialogs message off
	}
	DELETE SELECTION
	dialogs message on
	dialogs error on
	
	//終了
	message info  'Done'		
}

function GetRangeLst(int start, int count, output int list lst) {
	int num = 0
	$lst = {}
	do {
		int dmy = add_last($lst, $start + $num)
		$num = $num + 1
	} while $num < $count
}

function GetSelSurfCount(output int num) {
	string $TraceFilePath = macro_path(false) + "\selsurf.txt"
	string list txts = {}
	ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT
	TRACEFILE OPEN $TraceFilePath
	PRINT SELSURFACE
	TRACEFILE CLOSE
	ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
	
	FILE OPEN $TraceFilePath FOR READ AS Input
	FILE READ $txts FROM Input
	FILE CLOSE Input
	DELETE FILE $TraceFilePath

	$num = size($txts) - 1
}	

function ShowInfobox(string msg) {
	INFOBOX STYLE "NORMAL"
	INFOBOX APPEND  $msg
}

テスト的なコードのため、InfoBoxに結果を垂れ流しているだけです。
・・・遅いんです、処理が。

原因は
・ツールパスをアクティブにしている
・毎回、面を選択している
・選択面数の取得にファイルの書き込み・読み込みをしている
だとは思うのですが、アクティブにしなくても良かったのかな?
選択面数の取得はこちらの方法です。
水平面高さリストを表示2 - C#ATIA
ファイルの書み・読みを1ツールパスに付き16回もやっているんです・・・。

イロイロ探して試しているのですが、選択面数ぐらい何とか取得出来そうな
気もするのですが・・・。

マウスカーソルの座標値を取得する2

こちらの続きです。
マウスカーソルの座標値を取得する - C#ATIA

前回よくわからず、スコープを大きくした変数ですが、こんな感じにすると、
commandInputs が EventHandler の args から取得出来る事が判りました。
(commandInputs はVBAのフォームのようなもの)

class MyMouseMoveHandler(adsk.core.MouseEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.MouseEventArgs.cast(args)
        cmd = eventArgs.firingEvent.sender
        inputs = cmd.commandInputs

他もチョロチョロ修正し、こんな感じです。

#FusionAPI_python
#Author-kantoku
#Description-MouseMoveTest ver0.0.2

import adsk.core, traceback

_ui  = None
_handlers = []

#このコマンドのイベント・ダイアログ作成
class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            global _handlers
            cmd = adsk.core.Command.cast(args.command)

            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onMouseMove = MyMouseMoveHandler()
            cmd.mouseMove.add(onMouseMove)
            _handlers.append(onMouseMove)

            inputs = cmd.commandInputs
            
            inputs.addTextBoxCommandInput('Vp_Pos', 'Fusion360画面上の座標値', '-', 1, True)
            inputs.addTextBoxCommandInput('Sc_Pos', 'ディスプレイ上の座標値', '-', 1, True)
            inputs.addTextBoxCommandInput('3D_Pos', 'マウス3D仮座標値', '-', 1, True)
            inputs.addTextBoxCommandInput('Cam_Vec', 'カメラ向き(単位ベクトル)', '-', 1, True)
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
            
#このコマンドの破棄
class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            adsk.terminate()
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

#MouseMoveイベント
class MyMouseMoveHandler(adsk.core.MouseEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.MouseEventArgs.cast(args)
        cmd = eventArgs.firingEvent.sender
        inputs = cmd.commandInputs
        
        #ビューポイント
        vp = eventArgs.viewport
        
        #Fusion360画面上の座標値
        vppos = eventArgs.viewportPosition
        ui_vp = inputs.itemById('Vp_Pos')
        ui_vp.text = 'x:[%d] y:[%d]'%(vppos.x, vppos.y)
        
        #ディスプレイ上の座標値
        scrpos = vp.viewToScreen(vppos)
        ui_sc = inputs.itemById('Sc_Pos')
        ui_sc.text = 'x:[%d] y:[%d]'%(scrpos.x, scrpos.y)
        
        #マウス3D仮座標値-奥行きがNGな為正しくない(通過点)
        d3pos = vp.viewToModelSpace(vppos)
        d3_sc = inputs.itemById('3D_Pos')
        d3_sc.text = 'x:{:.3f} y:{:.3f} z:{:.3f}'.format(d3pos.x, d3pos.y, d3pos.z)
        
        #カメラ向き(単位ベクトル)
        cam = vp.camera
        vec = cam.eye.vectorTo(cam.target)
        vec.normalize()
        vec_sc = inputs.itemById('Cam_Vec')
        vec_sc.text = 'x:{:.3f} y:{:.3f} z:{:.3f}'.format(vec.x, vec.y, vec.z)        
        
def run(context):
    try:
        #global _app, _ui
        app = adsk.core.Application.get()
        _ui = app.userInterface
        
        cmdDef = _ui.commandDefinitions.itemById('Mouse_Move_Test')
        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition('Mouse_Move_Test', 'Mouse_Move_Test', 'Mouse_Move_Test')

        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()))

動作はこんな感じです。

マウス3D仮座標値はキューブの向きが上とかの向きになった場合のみ、
奥行き以外の座標値は正しく得られます。
ディスプレイが2Dな為に奥行きが確定せず、傾いた場合は全ての座標値が
正しく得られません。
マウスカーソルは3D空間上では、カメラ向き方向のベクトルでマウス3D仮座標値を
通過する状態になっているはず・・・確認していませんが。

カメラ向き方向のベクトルは、直接取得できるメソッドが無かった為、
カメラ位置からターゲットを単位ベクトル化しました。

        #カメラ向き(単位ベクトル)
        cam = vp.camera
        vec = cam.eye.vectorTo(cam.target)
        vec.normalize()

ターゲットが常に画面中央になっている事を前提で考えたのですが、
多分正しいよね?