C#ATIA

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

選択済みのツールパスからNCプログラムを作る

連日作業していて "作ろう作ろう" と思いながら放置していたマクロです。

"選択済みのツールパスから1個のNCプログラムを作る" だけです。

//pm2020 macro
//registerNCProgram.mac Ver0.0.1
//選択済みツールパスから1個のNCプログラム作成
//---------------------------------------------------------------------------------------------------
//<?xml version="1.0" ?>
//<menupage>
// <button label="NCプログラム作成" command='MACRO registerNCProgram.mac'  multiple_selection="allowed" />
//</menupage>
//---------------------------------------------------------------------------------------------------
//ver0.0.1:完成
//---------------------------------------------------------------------------------------------------

function main() {

	IF $PowerMILL.Status.MultipleSelection.Total  {
		IF $PowerMILL.Status.MultipleSelection.First == 0 {
			return
		}
	}

	//ツールパスリスト
	string list tps = extract(explorer_selected_entities() , 'name')

	// NCプログラム名
	string ncName = ''
	call getNcName($ncName)

	// NCプログラム作成
	call initNcpro($ncName)

	// ツールパスを追加
	call registerToolPath($ncName, $tps)

	// 終了
	DEACTIVATE NCProgram
	message info  'Done'
}

//ツールパスを追加
function registerToolPath(string ncName, string list tps){
	call Msgoff()
	foreach tp in $tps {
		EDIT NCPROGRAM $ncName INSERT Toolpath $tp LAST
	}
	call Msgon()
}

// NCプログラム作成
function initNcpro(string ncName){
	string tmp = new_entity_name('ncprogram')
	CREATE NCPROGRAM ; EDIT NCPROGRAM ; QUIT FORM NCTOOLPATH
	NCTOOLPATH ACCEPT FORM ACCEPT NCTOOLPATHLIST FORM ACCEPT NCTOOLLIST FORM ACCEPT PROBINGNCOPTS
	RENAME NCProgram $tmp $ncName
}

// NCプログラム名
Function getNcName(output string out) {
	string ncName = project_pathname(1)

	$ncName = input 'NCプログラム名を入力'
	if length($ncName) < 1 {
		macro about
	}
	call getNewName('ncprogram' , $ncName , $out)
}

// 新たな名前の取得
Function getNewName(string folder , string name , output string out) {
	if entity_exists(entity($folder ,$name)) < 1 {
		$out = $name
	} else {
		$out = new_entity_name($folder ,$name)
	}
	return
}

function Msgoff() {
	graphics lock
	dialogs message off
	dialogs error off
}

function Msgon() {
	graphics unlock
	dialogs message on
	dialogs error on
}

f:id:kandennti:20200114145828p:plain

途中でNCプログラム名を入力するダイアログが出ます。デフォルトは
プロジェクト名です。

ツールパス毎に作ってくれる機能はあるんですけどねぇ。

水平面だけを選択する

時間が無いので、取り急ぎ覚書。

PowerMillで水平面だけを選択するコマンドボタンが無いのですが、
コマンド自体はあります。

エコーコマンドで実行出来ます。

EDIT MODEL ALL SELECT FLAT

この一行のためにマクロにすべきか・・・。

ショートカットキーに空きがあれば登録したい。

閉空間球充填問題2

こちらの続きです。
閉空間球充填問題 - C#ATIA


問題を出しておいて答えがわかっていない無責任ぶりを少しでも
解消しようとn=6を正月辺りから取り組んでいます。


合成数だとイメージしやすいですね。6=2X3 なので。
イメージ出来る球体の配置は2種類ぐらい思いつくのですが、
まずこちら。
f:id:kandennti:20200107154253p:plain
XY平面な対角の位置に2個配置し、その上に90度回転させて
3段積みます。 でもこの配置だと
f:id:kandennti:20200107154309p:plain
半径25が限界だと直ぐにわかります。駄目ですね。


"それならば" っと平面的に3個並べて2段にしてみます。
f:id:kandennti:20200107154323p:plain
ところが、斜めから見てみると
f:id:kandennti:20200107154332p:plain
上の段と下の段で隙間があるんです。つまりもっと大きい球体が
入る余地があるんですよね。この時点で半径は25.4333095です。

但し余地があると言っても、既に3個は平面的な配置でのMAXなので
f:id:kandennti:20200107154343p:plain
半径を大きくした分、黄色は赤に寄って行きます。
当然赤は2面に接した状態のため、上に逃げるしかないのですが、
幸い上に逃げるためのスペースがあります。
問題はこの配置をどんなスケッチを描けば良いのか?と言うことに
なってしまい、困惑しまくりました。


悩んで悩んで、結果的に半径を少しづつ大きくしながら、
ピタゴラスの定理を利用したパラメータ作って、式を使って・・・
チマチマと人力ブルートフォースアタック的な方法で
作った結果、これぐらい持ち上がりました。
f:id:kandennti:20200107154352p:plain

スマートなスケッチが描けなかった・・・。せめてこちらに記載した
被駆動寸法値が利用出来るのであれば楽なのに。(CATIAは利用出来ます)
解決済み: 被駆動寸法値の利用 - Autodesk Community


進めながらちょっと感じたのは、こちらの正多面体の頂点数であれば、
配置のイメージが出来るかな?
正多面体 - Wikipedia
n^3とn^3+(n-1)^3以外だと、n=12(正二十面体)とn=20(正十二面体)が
楽なのかな? さらに中心に1個配置した13,21も楽なのかな?

閉空間球充填問題

あけましておめでとうございます。
毎年ながら、マイペースでやって行く予定です。

特に無いのですが、こちらの問題をフォーラムに
投げてみました。
forums.autodesk.com

1/6時点で1,2,4,9が解決済みです。
御興味ある方は是非挑戦してみてください。
(PC離れても頭の中で配置を考えるようになれば、本物です)

2019年を振り返る

通常、年末辺りは結構手が空くのですが、今年に限っては
忙しく、急いで振り返ります。

〇CATIA
正直なところネタ切れ感が拭えず、今年はほぼ書いて無い様な記憶です。
今年のネタでは無いのですが、こちらの手抜きメニュー
Free CAD Designs, Files & 3D Models | The GrabCAD Community Library
Win10+R2015での動作がイマイチで、マクロ実行直後に度々CATIAが
落ちていたのですが、R2018に切り替え後は安定しました。
(恐らくリフレクション辺りの動作が不安定だったように思えます)

逆にこちらが上手く動作しなくなりました。
DrawをPDFでエクスポート 3 - C#ATIA
年明け後、これを利用しそうなので直さねば。


Fusion360
ほとんどモデリング等を行わなかったのですが、時間があればAPI
触れるようにしていたので、一年前に比べ理解が深まりました。
成果物っぽいものは、これぐらいなのですが
GitHub - kantoku-code/Fusion360-TSplineBodyDoorway: Fusion360 TSplineBody Doorway
自身的にも非常に勉強になりました。


〇PowerMill
マクロ的には目新しいものは作った記憶が無いのですが、
地味なこちらマクロが業務的に助かってます。
GitHub - kantoku-code/PowerMill-PatternCleaner: PowerMill

残念な事に、サポートを今年で切ってしまい、バージョンアップが
出来なくなりました。文字化けを抱えたまま使用し続けなければ
ならないのが辛いところ・・・。


〇Space-e
今年いっぱいでライセンス自体を切りました。PC起動する度に
パスワード入力を強制するソフトとはおさらばです。



トータルとしては、Githubを利用するようになったことが大きいの
ですが、gitとしての利用と言うより "公開の場" として利用に
とどまっているので、もうちょっと変えていきたい?

良いお年を!

もっと短く書けるんじゃないかな?

海外の公開されているcatiaのマクロ(国内は、ほぼ無いけど)もそうなんですが、
Fusion360スクリプトも結構、読みにくいものが多い気がするんです。

とあるサイトで公開されていたものの一部なんですが

・・・
        line1 = adsk.core.Line3D.create(adsk.core.Point3D.create(33, 3, 0),
                                        adsk.core.Point3D.create(37, 3, 0))
        line2 = adsk.core.Line3D.create(adsk.core.Point3D.create(37, 3, 0),
                                        adsk.core.Point3D.create(37, 7, 0))
        line3 = adsk.core.Line3D.create(adsk.core.Point3D.create(37, 7, 0),
                                        adsk.core.Point3D.create(33, 7, 0))
        line4 = adsk.core.Line3D.create(adsk.core.Point3D.create(33, 7, 0),
                                        adsk.core.Point3D.create(33, 3, 0))

        line5 = adsk.core.Line3D.create(adsk.core.Point3D.create(33, 3, 2),
                                        adsk.core.Point3D.create(37, 3, 2))
        line6 = adsk.core.Line3D.create(adsk.core.Point3D.create(37, 3, 2),
                                        adsk.core.Point3D.create(37, 7, 2))
        line7 = adsk.core.Line3D.create(adsk.core.Point3D.create(37, 7, 2),
                                        adsk.core.Point3D.create(33, 7, 2))
        line8 = adsk.core.Line3D.create(adsk.core.Point3D.create(33, 7, 2),
                                        adsk.core.Point3D.create(33, 3, 2))
・・・

まぁインデントがそろっていて綺麗なんですが、ひたすら座標値を指定して
線を作っているだけなんですよね。

オブジェクトのプロパティやメソッドだって理解してれば、
こんな感じにしてしまっても良いと思う。

・・・
        lin3d = adsk.core.Line3D
        pnt3d = adsk.core.Point3D

        line1 = lin3d.create(pnt3d.create(33, 3, 0), pnt3d.create(37, 3, 0))
        line2 = lin3d.create(pnt3d.create(37, 3, 0), pnt3d.create(37, 7, 0))
        line3 = lin3d.create(pnt3d.create(37, 7, 0), pnt3d.create(33, 7, 0))
        line4 = lin3d.create(pnt3d.create(33, 7, 0), pnt3d.create(33, 3, 0))

        line5 = lin3d.create(pnt3d.create(33, 3, 2), pnt3d.create(37, 3, 2))
        line6 = lin3d.create(pnt3d.create(37, 3, 2), pnt3d.create(37, 7, 2))
        line7 = lin3d.create(pnt3d.create(37, 7, 2), pnt3d.create(33, 7, 2))
        line8 = lin3d.create(pnt3d.create(33, 7, 2), pnt3d.create(33, 3, 2))
・・・

もっとも、"lineX" の変数名に意味が有るとは思えないからリストにしても
良いだろうし、座標値見れば四角を2回描きたいのだろうと思うので、
それなりの関数用意しても良いように感じるんですよ。何度も行うのであれば。

”人の事、指摘出来る程のもの書いてるのか?" って言われると辛い。

同一半径限定で球体同士の交差を作る

同一半径限定で、表示された球体同士の交差を作ります。
・・・同一半径のみ欲しいので、同一半径のみなんです。

#Fusion360API Python script
#Author-kantoku
#Description-表示された球体を見つけ、交差作成(同一半径のみ)

import adsk.core, adsk.fusion, traceback
import math, itertools

_tolerance = 0

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

        global _tolerance
        um = des.unitsManager
        um.defaultLengthUnits
        _tolerance = um.convert(0.01, um.defaultLengthUnits, um.internalUnits)

        # 拡張
        adsk.fusion.BRepBody.isSphere = isSphere
        adsk.fusion.BRepBody.tryIntersect = tryIntersect
        adsk.core.Point3D.midPoint = midPoint3D

        adsk.core.Point3D.drawSkt = drawSketchPoint
        adsk.core.Circle3D.drawSkt = drawSketchCircle

        # 表示された球体を取得
        showBodies = getShowBody(des)
        spheres = [bd for bd in showBodies if bd.isSphere()]

        # 交差を取得
        ints = []
        for idx, sp1 in enumerate(spheres):
            for sp2 in spheres[idx + 1:]:
                ints.append(sp1.tryIntersect(sp2))
        if len(ints) < 1:
            msg  = '交差はありません!'
            ui.messageBox(msg)
            return
        
        # 可視化
        skt = initRootSketch()
        skt.arePointsShown = False
        skt.isComputeDeferred = True
        [i[0].drawSkt(skt) for i in  ints if len(i) > 0]
        skt.isComputeDeferred = False
        
        # おしまい
        app.activeViewport.refresh()
        ui.messageBox('Done')

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

def drawSketchCircle(
    self :adsk.core.ObjectCollection,
    skt :adsk.fusion.Sketch):

    skt.sketchCurves.sketchFittedSplines.addByNurbsCurve(self.asNurbsCurve)

def drawSketchPoint(
    self :adsk.core.Point3D,
    skt :adsk.fusion.Sketch):

    skt.sketchPoints.add(self)

def initRootSketch() -> adsk.fusion.Sketch:
    app  :adsk.core.Application = adsk.core.Application.get()
    des  :adsk.fusion.Design = app.activeProduct
    root :adsk.fusion.Component = des.rootComponent

    return root.sketches.add(root.xYConstructionPlane)

def getShowBody(
    des :adsk.fusion.Design):

    return [bBody
        for comp in des.allComponents if comp.isBodiesFolderLightBulbOn
        for bBody in comp.bRepBodies if bBody.isLightBulbOn & bBody.isVisible]

def isSphere(
    self :adsk.fusion.BRepBody) -> bool:

    # すべて球面か?
    sr = adsk.core.SurfaceTypes.SphereSurfaceType
    if len([fc for fc in self.faces if fc.geometry.surfaceType != sr]) > 0:
        return False

    # 半径は一致しているか?
    if len(set([round(fc.geometry.radius, 4) for fc in self.faces])) > 1:
        return False

    # 面積は正しいか?
    r = self.faces.item(0).geometry.radius
    if abs(4 * math.pi * r * r - self.area) > 0.001:
        return False

    return True

def tryIntersect(
    self :adsk.fusion.BRepBody,
    tgt :adsk.fusion.BRepBody):

    tgtSurfs = tgt.faces
    spSurfs = self.faces

    app  :adsk.core.Application = adsk.core.Application.get()

    ints = []
    for tgtSurf in tgtSurfs:
        tgtGeo = adsk.core.Sphere.cast(tgtSurf.geometry)
        tgtOri :adsk.core.Point3D = tgtGeo.origin
        tgtRad = tgtGeo.radius

        for spSurf in spSurfs:
            spGeo = adsk.core.Sphere.cast(spSurf.geometry)
            if abs(spGeo.radius - tgtRad) > _tolerance:
                return None
            
            spOri = spGeo.origin
            minLength = tgtOri.distanceTo(spOri)
            if minLength > tgtRad * 2:
                # 干渉なし
                continue
            
            if minLength < _tolerance:
                # 同一
                continue

            if abs(minLength - tgtRad * 2) < _tolerance:
                # 接触
                ints.append(spOri.midPoint(tgtOri))
            else:
                # 干渉
                vec = spOri.asVector()
                vec.subtract(tgtOri.asVector())
                ori = spOri.midPoint(tgtOri)
                pln = adsk.core.Plane.create(ori, vec)

                interLst = pln.intersectWithSurface(spGeo)
                [ints.append(c) for c in interLst] 

    return ints

def midPoint3D(
    self :adsk.core.Point3D,
    pnt :adsk.core.Point3D) -> adsk.core.Point3D:

    p = self.copy()
    p.setWithArray([(x + y) * 0.5 for (x, y) in zip(self.asArray(), pnt.asArray())])
    return p

イマイチな部分が多々有り。

こんなデータで実行すると
f:id:kandennti:20191224145435p:plain

こんな感じで
f:id:kandennti:20191224145528p:plain