選択した円の中心座標を取得する(Product.ver)|CATIAマクロの作成方法
今回は「お問い合わせ」から頂いた内容です。
以前記事にした「選択した円の中心座標を取得するマクロ」で少し問題があったようです。
先日の記事でエッジの中心座標は求めることができたのですが、今回ほしかった座標というのが、プロダクト上での絶対座標であり、パート上の座標ではなかったのです。
以前のマクロで取得する中心座標はどうもCATPartの座標系を基準にしているようで、CATProductでCATPartの配置情報を変えたとしても同じ中心座標が取得されてしまう状態になっています。
というわけで、今回は以前紹介したコードを「CATProductの絶対座標系を基準とした円の中心座標を取得するためのコード」に書き換えていきたいと思います。
※今回のコードはCATProductには対応していますがCATPart単体には対応していません。
両ドキュメントを対応させるにはTypeName関数を使った条件分岐などで行えると思います。
マクロ作成の前に
まず大前提して”CATProductの座標系を基準とした座標”を取得するためのメソッドは存在しないようです。(実際はあるのかもしれませんが)
そこで、どのようにすればCATProductの座標系で座標を取得できるかを色々調べていると、以下のページで本件と同じ内容が語られていました。
Cannot get global(assembly) coordinates – COE : Forums
上記ページにもそのようなメソッドは無いと語られていますが、あわせて以下のような解決案も語られていました。
If you want these coordinates given with respect to the CATProduct’s axis system you must apply an axis system transformation to the coordinates.
(座標をCATProductの軸系を基準にして与えたい場合は、座標に軸系変換を適用する必要があります。)
要はCATPartの座標系で取得した座標を、『行列』を使ってCATProductの座標系を基準とした座標に変換しようということです。
上記ページではその座標変換のための計算コードも書かれていたので、本ページではそのコードを解説しつつコピペでも使えるように少し書き直して紹介していきます。
完成コード
完成コードは以下の通りです。
※実行するプロシージャはCATMainの部分です。
コピペで実行可能ですがエラー確認はあまりしていません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
Sub CATMain() Dim DOC As ProductDocument Set DOC = CATIA.ActiveDocument Dim Sel Set Sel = DOC.Selection Dim InputType(0) InputType(0) = "Edge" Dim Msg As String Msg = "穴(エッジ)を選択してください。" Dim Status As String Status = Sel.SelectElement2(InputType, Msg, False) If ((Status = "Cancel") Or (Status = "Undo")) Then Exit Sub End If Dim SelEdge As AnyObject Set SelEdge = Sel.Item(1).Value Dim oProduct As Product Set oProduct = Sel.Item(1).LeafProduct '選択したエッジを含んでいるProductを取得 Dim SPA As Workbench Set SPA = DOC.GetWorkbench("SPAWorkbench") Dim MyMeasure Set MyMeasure = SPA.GetMeasurable(SelEdge) Dim getvalues(2) As Variant 'X,Y,Z座標を取得する用の配列を用意(Variant型) MyMeasure.GetCenter getvalues 'MeasureableオブジェクトのGetCenterメソッドを使用 Dim aRel(2) As Double '取得した座標をDouble型に変換(行列計算のコードで使用可能にするため) aRel(0) = getvalues(0) aRel(1) = getvalues(1) aRel(2) = getvalues(2) Dim aAbs() As Double '行列計算結果を受け取る用の配列を用意(中身は空) Call Coord_Transform(aRel(), aAbs(), oProduct, True) '行列計算 Dim OutputCoord(2) OutputCoord(0) = Format(aAbs(0), "0.000") 'X座標(少数第4位を四捨五入) OutputCoord(1) = Format(aAbs(1), "0.000") 'Y座標(少数第4位を四捨五入) OutputCoord(2) = Format(aAbs(2), "0.000") 'Z座標(少数第4位を四捨五入) MsgBox "X座標 = " & OutputCoord(0) & vbLf & _ "Y座標 = " & OutputCoord(1) & vbLf & _ "Z座標 = " & OutputCoord(2) End Sub '--------------------------------------------------------------------------------------------------------' Sub Coord_Transform(aRel() As Double, aAbs() As Double, oProduct As Product, bRecursively As Boolean) Dim vProduct As Object, vCoord(11) Dim oFatherProduct As Product Dim aInv() As Double 'Exit condition, empty object If oProduct Is Nothing Then Exit Sub 'Redim absolute coords matrix On Error Resume Next ReDim aAbs(2) On Error GoTo 0 'Calculate product coordinates Set vProduct = oProduct vProduct.Position.GetComponents vCoord 'Calculate inverse matrix If Inv3x3(CDbl(vCoord(0)), CDbl(vCoord(1)), CDbl(vCoord(2)), _ CDbl(vCoord(3)), CDbl(vCoord(4)), CDbl(vCoord(5)), _ CDbl(vCoord(6)), CDbl(vCoord(7)), CDbl(vCoord(8)), aInv) Then Else MsgBox "Error, degenerate transformation", vbOKOnly Exit Sub End If 'Calculate transformation aAbs(0) = vCoord(9) + aInv(0) * aRel(0) + aInv(1) * aRel(1) + aInv(2) * aRel(2) aAbs(1) = vCoord(10) + aInv(3) * aRel(0) + aInv(4) * aRel(1) + aInv(5) * aRel(2) aAbs(2) = vCoord(11) + aInv(6) * aRel(0) + aInv(7) * aRel(1) + aInv(8) * aRel(2) 'If recursive option sepecified, search for parents and applies the transformation again If bRecursively Then 'Try to assign parent Set oFatherProduct = Nothing On Error Resume Next Set oFatherProduct = oProduct.Parent.Parent On Error GoTo 0 'If OK, recalculate coords If oFatherProduct Is Nothing Then Else aRel(0) = aAbs(0) aRel(1) = aAbs(1) aRel(2) = aAbs(2) Coord_Transform aRel, aAbs, oFatherProduct, True End If End If End Sub '--------------------------------------------------------------------------------------------------------' Function Inv3x3(dX11 As Double, dX12 As Double, dX13 As Double, _ dX21 As Double, dX22 As Double, dX23 As Double, _ dX31 As Double, dX32 As Double, dX33 As Double, aInv() As Double) As Boolean '*********************************************** '* '* 3x3 matrix inverse calculation (direct) '* '*********************************************** Dim dDet As Double ReDim aInv(8) Inv3x3 = False dDet = Det3x3(dX11, dX12, dX13, dX21, dX22, dX23, dX31, dX32, dX33) If dDet = 0 Then Exit Function aInv(0) = (dX22 * dX33 - dX23 * dX32) / Abs(dDet) aInv(1) = (dX13 * dX32 - dX12 * dX33) / Abs(dDet) aInv(2) = (dX12 * dX23 - dX13 * dX22) / Abs(dDet) aInv(3) = (dX23 * dX31 - dX21 * dX33) / Abs(dDet) aInv(4) = (dX11 * dX33 - dX13 * dX31) / Abs(dDet) aInv(5) = (dX13 * dX21 - dX11 * dX23) / Abs(dDet) aInv(6) = (dX21 * dX32 - dX22 * dX31) / Abs(dDet) aInv(7) = (dX12 * dX31 - dX11 * dX32) / Abs(dDet) aInv(8) = (dX11 * dX22 - dX12 * dX21) / Abs(dDet) Inv3x3 = True End Function '--------------------------------------------------------------------------------------------------------' Function Det3x3(dX11 As Double, dX12 As Double, dX13 As Double, _ dX21 As Double, dX22 As Double, dX23 As Double, _ dX31 As Double, dX32 As Double, dX33 As Double) As Double '*********************************************** '* '* 3x3 matrix determinant calculation (direct) '* '*********************************************** Det3x3 = dX11 * dX22 * dX33 + dX12 * dX23 * dX31 + dX21 * dX32 * dX13 - _ dX13 * dX22 * dX31 - dX12 * dX21 * dX33 - dX23 * dX32 * dX11 End Function |
かなり長いコードですが、55行以降の内容はすべて先ほどのページのコードをそのままコピペしたもので、行列の計算をするためだけのコードとなっています。
マクロの使用方法は前回と同じで、中心座標を取得したい円のエッジを選択するだけです。
※CATPartが表示モード(Visualization Mode)の場合、エッジを選択することができません。
エッジを選択するCATPartだけは設計モード(Design Mode)に切り替える必要があります。
コード解説
ここでは上記のコードをざっくりと処理別に分けて簡単に説明していきます。
(主に座標変換のコードについての内容ですが)
座標変換のコードは元のページでも説明されているため合わせて読んでみて下さい。
Cannot get global(assembly) coordinates – COE : Forums
英語が苦手な方は「DeepL翻訳」を使えばほとんどを理解できるレベルで翻訳してくれます。
中心座標の取得
まず始めに選択した正円エッジから中心座標を取得します。
中心座標を取得する方法は「選択した円の中心座標を取得するマクロ」を参照ください。
取得できる座標はCATPartの座標となってしまいますが、現時点では問題ありません。
この取得した座標を以下の方法で座標変換しCATProductでの座標に変換します。
座標変換の計算
座標変換を行うには行列の計算を行う必要があります。
(コード的には55行以降がすべて座標変換の計算式)
まず大前提としてCATProductの最上位のProduct(RootProduct)の座標系の成分を取得し、行列のように表示すると以下のようになります。
そして、CATProduct上で適当に回転させたCATPartの座標系の成分は以下のように取得することができます。(この数値はCATPartの配置情報によって変動します)
上記の前提を理解したうえで行列の公式をみてみましょう。
行列A×行列Aの逆行列=単位行列
単位行列とはCATProductで表示したような対角成分が1でそれ以外が0である行列のことをいいます。
つまり行列AをCATPartの座標系としたとき、「行列Aの逆行列」さえ求めることができればCATProductの座標系に変換ができることがわかります。
逆行列を求めるには以下の公式を使います。
(※公式の詳しい解説は「逆行列 公式」等で検索してみて下さい)
これにより逆行列を求めることも可能なことがわかりました。
後は計算で求めるってことですね!
いろいろ書いてきましたがここでの内容を一言でまとめると
CATPartの座標系の逆行列を求めて、CATPartの座標系の行列にかけるということです。
計算コードの確認
座標変換がどのように行われているかを理解したところでコードを見ていきます。
メインプロシージャで「Coord_Transform」を呼び出せば計算が行われます。
Coord_Transformで必要な材料は以下のとおりです。
aRel() As Double ⇒ 変換したい座標の入った配列
aAbs() As Double ⇒ 配列だけ宣言しておけばOK(ここに計算結果が入る)
oProduct As Product ⇒ エッジを含むCATPart
bRecursively As Boolean ⇒ TrueにしておけばOK
詳しくはこのコードが書かれていたページを参照ください。
「Coord_Transform」の処理の流れはざっくりいうと以下のとおりです。
① CATPartの座標系の逆行列を求める
「Coord_Transform」内の「Function Inv3x3」では先ほどの公式を使い逆行列を求めています。
ただ公式をそのまますべて書くとコードがごちゃごちゃするため「Function Det3x3」を用意して複数に分けています。ちなみに「Function Det3x3」では公式の分母の部分の計算をしているだけです。
② 取得した中心座標を逆行列を使って変換する
最終的にはここで求めた逆行列を使って座標変換を行っています。
‘Calculate transformation
aAbs(0) = vCoord(9) + aInv(0) * aRel(0) + aInv(1) * aRel(1) + aInv(2) * aRel(2)
aAbs(1) = vCoord(10) + aInv(3) * aRel(0) + aInv(4) * aRel(1) + aInv(5) * aRel(2)
aAbs(2) = vCoord(11) + aInv(6) * aRel(0) + aInv(7) * aRel(1) + aInv(8) * aRel(2)
変換後の座標はaAbs()に入ります。
※aAbs(0) → X座標 、aAbs(1) → Y座標 、aAbs(2) → Z座標
取得した座標をユーザー向けに変換
上記のままでは変換した座標は非常に細かい数値で取得されてしまいます。
このままではメッセージボックスで表示してもゴチャゴチャしますし、計算するにしてもユーザー目線で考えると非常に扱いづらいです。
そこでCATIAのデフォルトの有効数字の小数第3位になるように小数第4位を四捨五入します。
OutputCoord(0) = Format(aAbs(0), “0.000”) ‘X座標(少数第4位を四捨五入)
OutputCoord(1) = Format(aAbs(1), “0.000”) ‘Y座標(少数第4位を四捨五入)
OutputCoord(2) = Format(aAbs(2), “0.000”) ‘Z座標(少数第4位を四捨五入)
“0.000”の部分を”0.00″にすれば小数第3位を四捨五入、”0.0000″にすれば小数第5位を四捨五入となります。取得した座標の使用用途に合わせて任意で書き換えましょう。
最終的に取得した座標はOutputCoord()の中に格納されています。
※OutputCoord(0) → X座標 、OutputCoord(1) → Y座標 、OutputCoord(2) → Z座標
まとめ
今回はCATProduct上で選択したエッジ(正円)の中心座標値を取得すマクロの紹介でした。
前回のマクロに比べかなり内容が濃くなりましたがやっていることはあまり難しいものではありません。取得したCATPartの座標系を単位行列、つまりはCATProductの座標系に変換するために計算をしているだけです。
マクロさわりたての方は理解するまでに時間がかかると思いますが、1文ずつどのような処理がされているのかを確認していけば必ず理解はできます。前回のマクロと合わせて今回の応用的な内容も勉強してみて下さい。