こちらの続きです。
ボディの体積をダンプする4 - C#ATIA
中途半端な状態で止まっていましたが、やっと取り組めました。
・CATIAの場合、体積が単純なプロパティでは取得出来ない
・VBAのクラスに拡張プロパティのような考えが無い
を考慮すると、
ソート関数(ソート対象の配列, 比較のための配列)
のような関数を作る事にしました。
'vba Option Explicit Sub CATMain() Dim doc As partDocument Set doc = CATIA.ActiveDocument Dim pt As Part Set pt = doc.Part 'ボディを配列として取得 Dim bodyAry As Variant bodyAry = get_bodies_array(pt.bodies) '体積配列を取得 - ボディの配列のインデックスと一致 Dim volumeAry As Variant volumeAry = get_bodies_volume(bodyAry) '確認 dump_pair bodyAry, volumeAry, "-- ソート前 --" 'ソート Dim sortedBodyAry As Variant sortedBodyAry = insertion_sort(bodyAry, volumeAry) 'ソート後の体積配列取得 volumeAry = get_bodies_volume(sortedBodyAry) '確認 dump_pair sortedBodyAry, volumeAry, vbCrLf & "-- ソート後 --" End Sub Private Function insertion_sort( _ ByVal targetAry As Variant, _ ByVal comparisonAry As Variant _ ) As Variant Dim zip() As Variant zip = zip_array(targetAry, comparisonAry) Dim low As Long low = LBound(zip) Dim upp As Long upp = UBound(zip) Dim i As Long, j As Long Dim tmp As Variant For i = low + 1 To upp tmp = zip(i) For j = i - 1 To low Step -1 If zip(j)(1) > tmp(1) Then zip(j + 1) = zip(j) Else Exit For End If Next zip(j + 1) = tmp Next Dim unzip() As Variant unzip = unzip_array(zip) insertion_sort = unzip(0) End Function Private Function unzip_array( _ ByVal ary As Variant _ ) As Variant Dim ary1() As Variant ary1 = create_array(UBound(ary)) Dim ary2() As Variant ary2 = create_array(UBound(ary)) Dim i As Long For i = 0 To UBound(ary) Set ary1(i) = ary(i)(0) ary2(i) = ary(i)(1) Next unzip_array = Array(ary1, ary2) End Function Private Function zip_array( _ ByVal ary1 As Variant, _ ByVal ary2 As Variant _ ) As Variant Dim zip() As Variant zip = create_array(UBound(ary1)) Dim i As Long For i = 0 To UBound(ary1) zip(i) = Array(ary1(i), ary2(i)) Next zip_array = zip End Function Private Function create_array( _ count As Long _ ) As Variant Dim ary() As Variant ReDim ary(count) create_array = ary End Function Private Function get_bodies_volume( _ ByVal bodyAry As Variant) _ As Variant get_bodies_volume = Array() If UBound(bodyAry) < 1 Then Exit Function Dim pt As Part Set pt = get_parent_of_T(bodyAry(0), "Part") Dim volumeAry() As Variant volumeAry = create_array(UBound(bodyAry)) Dim spaWb As SPAWorkbench Set spaWb = pt.Parent.GetWorkbench("SPAWorkbench") Dim bdy As Body Dim bodyRef As Reference Dim meas As Measurable Dim shape As AnyObject Dim volume As Double Dim i As Long For i = 0 To UBound(bodyAry) Set bdy = bodyAry(i) Set shape = get_last_shape(bdy) If shape Is Nothing Then volume = 0 Else Set bodyRef = pt.CreateReferenceFromObject(shape) Set meas = spaWb.GetMeasurable(bodyRef) volume = meas.volume End If volumeAry(i) = volume Next get_bodies_volume = volumeAry End Function Private Function get_bodies_array( _ ByVal bodyLst As bodies) _ As Variant Dim ary() As Variant ary = create_array(bodyLst.count - 1) Dim i As Long For i = 1 To bodyLst.count Set ary(i - 1) = bodyLst.Item(i) Next get_bodies_array = ary End Function Private Function get_last_shape( _ ByVal bdy As Body) _ As AnyObject Set get_last_shape = Nothing If bdy.shapes.count < 1 Then Exit Function Dim pt As Part Set pt = get_parent_of_T(bdy, "Part") Dim shapes As shapes Set shapes = bdy.shapes Dim i As Long For i = shapes.count To 1 Step -1 If Not IsEmpty(shapes.Item(i)) Then If False = pt.IsInactive(shapes.Item(i)) Then Set get_last_shape = shapes.Item(i) Exit Function End If End If Next End Function 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 = asDisp(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 Private Function asDisp( _ o As INFITF.CATBaseDispatch) _ As INFITF.CATBaseDispatch Set asDisp = o End Function Private Sub dump_pair( _ ByVal bodyAry As Variant, _ ByVal volumeAry As Variant, _ Optional ByVal msg = "") dump msg Dim i As Long For i = 0 To UBound(bodyAry) dump bodyAry(i).name & " : " & Format(volumeAry(i), "0.00000000000000000000000") Next End Sub Private Sub dump( _ ByVal msg As String) Debug.Print msg End Sub
試しにこの様な適当なデータを作成し実行してみます。
結果はこちら。一応出来ています。
-- ソート前 -- パーツ ボディー : 0.00077597338543667900000 ボディー.3 : 0.00026000000000000000000 ボディー.4 : 0.00056500000000000000000 ボディー.5 : 0.00006338919271492180000 ボディー.6 : 0.00000031415926535897900 -- ソート後 -- ボディー.6 : 0.00000031415926535897900 ボディー.5 : 0.00006338919271492180000 ボディー.3 : 0.00026000000000000000000 ボディー.4 : 0.00056500000000000000000 パーツ ボディー : 0.00077597338543667900000
・・・指数表記だとパッと見で判断しにくかったですが、
どうやるのが良いのだろう?
これ、安全か? と言われるとちょっと例外処理やら、
配列の先頭のインデックスの判断とか、zipライクな関数で
二つの配列サイズが未チェックだとか省いているので、
ちょっと怪しいです。
仮に、体積ではなく表面積にしたいのであれば、
insertion_sort関数の第二引数の配列を変更すれば良いので、
多少は柔軟性を持っているはず。
クイックソートで無いのですが、CATIAでソートが必要になる場面は
それ程大きいサイズにならないと思うので、大丈夫じゃないかな?