C#ATIA

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

形状セットの深さを表示

この様な形状セットがあります。

このPart3の深さを示すための文字列を作りたいです。
こんな感じの文字列です。

Product3\Product2\Part3

あぁ¥マークが変わっちゃう・・・。

こんな感じにしました。

'vba
Option Explicit


Sub CATMain()

    Dim pDoc As partDocument
    Set pDoc = CATIA.ActiveDocument

    Dim pt As Part
    Set pt = pDoc.Part
    
    Dim hBody As HybridBody
    Set hBody = pt.HybridBodies.Item(2).HybridBodies.Item(1)
    
    
    Debug.Print get_full_path(hBody, "")

End Sub


Private Function get_full_path( _
    ByVal target As AnyObject, _
    ByVal path As String) As String
    
    If is_part(target) Then
        Dim pt As Part
        Set pt = target
        get_full_path = pt.name & "\" & path
        Exit Function
    End If
    
    get_full_path = get_full_path( _
        target.parent, _
        target.name & "\" & path _
    )

End Function


Private Function is_part( _
    ByVal target As AnyObject) As Boolean
    
    Dim tmp As Part
    
    On Error Resume Next
    
    Set tmp = target
    
    On Error GoTo 0

    is_part = IIf(tmp Is Nothing, False, True)
    
End Function

結果はこんな何時です。

Product3\Product2\Part3\

最後がちょっと余計ですね・・・。

VBAエディタの取得

タイトル異なりますが、こちらの続きです。
CATVBAの標準モジュールをマクロで削除したい (希望)2 - C#ATIA
もう、7年も前の話だ・・・。


ちょっと先々の事を考えて、VBAのエディターを操作したくなる
気がしたので、復習がてらExcelでも利用出来るようにと考えており、
好みの部分を含めて、こんな感じに修正しました。

'vba
'現在開かれているVBプロジェクト数を表示
'事前に参照設定する必要ありません
'実行環境がexcelで無い場合は、catiaと判断

Option Explicit

Sub get_vbe_sample()

    Dim vbe As Object
    Set vbe = get_vbe
    If vbe Is Nothing Then
        MsgBox "VBAエディターが取得できませんでした"
        Exit Sub
    End If

    MsgBox "現在開かれているプロジェクト数は" & vbe.VBProjects.Count & "個です"
    
End Sub


'アプリケーション別でVBEditor取得
Private Function get_vbe() As Object

    Dim vbe As Object

    On Error Resume Next
        Dim app As Application
        Set app = Application
        If app.Name = "Microsoft Excel" Then
            'excel
            Set vbe = app.vbe
        Else
            'catia
            Set vbe = get_vbe_by_catia()
        End If
    On Error GoTo 0

    Set get_vbe = vbe

End Function


'CATIAでVBEditor取得
Private Function get_vbe_by_catia() As Object

    Set get_vbe = Nothing
    
    'VBAのバージョンチェック
    Dim comObjectName As String
    #If VBA6 Then
        comObjectName = "MSAPC.Apc.6.2"
    #ElseIf VBA7 Then
        comObjectName = "MSAPC.Apc.7.1"
    #Else
        'VBAのバージョンが未対応
        Exit Function
    #End If
    
    'APC取得
    Dim apc As Object
    Set apc = Nothing
    On Error Resume Next
        Set apc = CreateObject(comObjectName)
    On Error GoTo 0
    
    'VBE取得
    If apc Is Nothing Then
        Exit Function
    End If
    
    Set get_vbe = apc.vbe

End Function

実行結果はこちら。

えぇ、Excelで試しました。 ので、CATIAでは未テストです。

Excelの場合は、VBAエディタが簡単に取得出来るんですね。

自作2Dの点とベクトル

前にも作ったのですが、やっぱり必要性を感じるので少し育てました。

catiaのクラス名とバッティングする為、アンダーバーを入れることにしました。

点クラスです。トレランス付きの一致を追加しています。

'vba Point_2D

Option Explicit

Private x_ As Double
Private y_ As Double


Private Sub Class_Initialize()
    x_ = 0
    y_ = 0
End Sub


Private Sub Class_Terminate()
    
End Sub


'**プロパティ**
Public Property Get x() As Double
    x = x_
End Property


Public Property Let x(ByVal value As Double)
    x_ = value
End Property


Public Property Get y() As Double
    y = y_
End Property


Public Property Let y(ByVal value As Double)
    y_ = value
End Property


'**メソッド**
'2点間ベクトル
Public Function vector_to(ByVal point As Point_2D) As Vector_2D
    Dim vec As Vector_2D
    Set vec = New Vector_2D
    
    vec.with_array Array(point.x - x_, point.y - y_)
    
    Set as_vector = vec
End Function


'2点間距離
Public Function distance_to(ByVal point As Point_2D) As Double
    distance_to = Sqr((point.x - x_) ^ 2 + (point.y - y_) ^ 2)
End Function


'クローン
Public Function clone() As Point_2D
    Dim pnt As Point_2D
    Set pnt = New Point_2D
    pnt.with_array (Array(x_, y_))

    Set clone = pnt
End Function


'ベクトル化
Public Function as_vector() As Vector_2D
    Dim vec As Vector_2D
    Set vec = New Vector_2D
    
    vec.with_array Array(x_, y_)
    
    Set as_vector = vec
End Function


'配列化
Public Function as_array() As Variant
    as_array = Array(x_, y_)
End Function


'移動
Public Sub translate_by(ByVal vector As Vector_2D)
    x_ = x_ + vector.x
    y_ = y_ + vector.y
End Sub


'配列で設定
Public Sub with_array(ByVal ary As Variant)
    x_ = ary(0)
    y_ = ary(1)
End Sub


'文字
Public Function str() As String
    str = x_ & "," & y_
End Function


'一致か?
Public Function is_equal( _
    ByVal pnt As Point_2D, _
    Optional ByVal tolerance As Double = 0.001) As Boolean

    is_equal = Me.distance_to(pnt) < tolerance
End Function

今回は要らないような気もしますが、ベクトルです。
点同様に一致と長さのプロパティを追加です。

'vba Vector_2D

Option Explicit

Private x_ As Double
Private y_ As Double


Private Sub Class_Initialize()
    x_ = 0
    y_ = 0
End Sub


Private Sub Class_Terminate()
    
End Sub


'**プロパティ**
Public Property Get x() As Double
    x = x_
End Property


Public Property Let x(ByVal value As Double)
    x_ = value
End Property


Public Property Get y() As Double
    y = y_
End Property


Public Property Let y(ByVal value As Double)
    y_ = value
End Property


Public Property Get length() As Double
    length = Sqr(x_ * x_ + y_ * y_)
End Property


'**メソッド**

'クローン
Public Function clone() As Point_2D
    Dim vec As Vector_2D
    Set vec = New Vector_2D
    
    vec.with_array Array(x_, y_)
    
    Set clone = vec
End Function


'ポイント化
Public Function as_point() As Point_2D
    Dim pnt As Point_2D
    Set pnt = New Point_2D
    
    pnt.with_array Array(x_, y_)
    
    Set as_point = pnt
End Function


'配列化
Public Function as_array() As Variant
    as_array = Array(x_, y_)
End Function


'外積
Public Function cross(ByVal vector As Vector_2D) As Double
    cross = x_ * vector.x - y_ * vector.y
End Function


'内積
Public Function dot(ByVal vector As Vector_2D) As Double
    dot = x_ * vector.x + y_ * vector.y
End Function


'差
Public Sub subtract(ByVal vector As Vector_2D)
    x_ = x_ - vector.x
    y_ = y_ - vector.y
End Sub


'和
Public Sub add(ByVal vector As Vector_2D)
    x_ = x_ + vector.x
    y_ = y_ + vector.y
End Sub


'スカラー倍
Public Sub scale_by(ByVal ratio As Double)
    x_ = x_ * ratio
    y_ = y_ * ratio
End Sub


'単位化
Public Function normalize() As Boolean
    Dim length As Double
    length = Me.length
    
    If length <= 0 Then
        normalize = False
        Exit Function
    End If

    x_ = x_ / length
    y_ = y_ / length

    normalize = True
End Function


'配列で設定
Public Sub with_array(ByVal ary As Variant)
    x_ = ary(0)
    y_ = ary(1)
End Sub


'文字
Public Function str() As String
    str = x_ & "," & y_
End Function


'一致か?
Public Function is_equal( _
    ByVal vec As Vector_2D, _
    Optional ByVal tolerance As Double = 0.001) As Boolean

    is_equal = Me.as_point().distance_to( _
        vec.as_point()) < tolerance
End Function

主に新たな機能についての単体テスト

'vba 

Sub unit_test()

    Dim p1 As Point_2D
    Set p1 = New Point_2D
    p1.with_array Array(1, 2)

    Dim p2 As Point_2D
    Set p2 = New Point_2D

    p2.with_array Array(2, 2)
    Debug.Assert p1.is_equal(p2) = False

    p2.with_array Array(1, 2)
    Debug.Assert p1.is_equal(p2) = True

    p2.with_array Array(1.0001, 2)
    Debug.Assert p1.is_equal(p2, 0.01) = True


    Dim vec1 As Vector_2D
    Set vec1 = New Vector_2D
    
    vec1.with_array (Array(2, 0))
    Debug.Assert vec1.length = 2

    vec1.with_array (Array(2, 1))
    Debug.Assert Not vec1.length = 2

    vec1.normalize
    Debug.Assert vec1.length = 1

    Dim vec2 As Vector_2D
    Set vec2 = New Vector_2D
    
    vec1.with_array (Array(2, 0))
    vec2.with_array (Array(2, 0))
    Debug.Assert vec1.is_equal(vec2)

    vec2.scale_by -1
    Debug.Assert vec1.is_equal(vec2) = False

    Debug.Print "OK"

End Sub

OKです。

Period

また書きかけているのに放置してますね・・・。

ちょっと疑問に思った事がありまして、試しました。

まず、このような円や円弧をDrawで描きます。

続いてこんなマクロ(VBA)を作成しました。

'vba
Option Explicit

Sub CATMain()

    Dim sel As Variant 'Selection
    Set sel = CATIA.ActiveDocument.Selection

    Dim msg As String
    msg = "選択して下さい : ESCキー 終了"

    Dim selectObj As AnyObject
    Do
        sel.Clear
        Select Case sel.SelectElement2(Array("AnyObject"), msg, False)
            Case "Cancel", "Undo", "Redo"
                Exit Sub
        End Select

        Set selectObj = sel.Item2(1).value

        Stop
    Loop
End Sub

何かを選択してストップするだけです。

これで左の円を選択し、ローカルウィンドウを見ると見覚えのない
プロパティが。

Period・・・言葉の意味がわからないので調べると"期間""周期"って
出てきます。

ドキュメントのCircle2Dを見ても出てこないプロパティです。
r1 Circle2D (Object)

と思ったら、Curve2Dに有りました。
r1 Curve2D (Object)


但し、数値の"6.28318530717959"には見覚えがあります。
これは2π(円周率)ですね。つまりパラメータです。
右の円弧を調べても同じでした。

こんな感じでもう少し線を追加しました。

それぞれ試してみると
・楕円:6.28318530717959
・スプライン:それなりに大きな数値
・直線:0
楕円も6.28318530717959なんですね。まぁ確かに。
と考えるとPeriodだけでは円とは判断できないですね。

唯一直線の判断は出来ますが・・・それ調べなくてもわかるかな。

"プロパティをリセット"コマンドをマクロで実行3

こちらの続きです。
"プロパティをリセット"コマンドをマクロで実行2 - C#ATIA

pythonではイマイチだった為、vbaに戻って再挑戦。
vbaの場合は、UI Automationを使います・・・デバッグしにくい。

こちらを参照しながら、もがき苦しんだ末、出来たものがこちらです。
Graph Tree Reordering in VBA | Scripts4All

'vba
'要参照設定 UIAutomationClient

Option Explicit

Sub CATMain()

    Dim pDoc As PartDocument
    Set pDoc = CATIA.ActiveDocument
    
    Dim pt As part
    Set pt = pDoc.part
    
    Dim sel As Selection
    Set sel = pDoc.Selection
    
    Dim bdy As body
    sel.Clear
    For Each bdy In pt.bodies
        sel.Add bdy
    Next
    
    CATIA.StartCommand "プロパティをリセット"
    CATIA.RefreshDisplay = True
    
    Dim winAutomation As CUIAutomation
    Set winAutomation = New CUIAutomation
    
    Dim desktop As IUIAutomationElement
    Set desktop = winAutomation.GetRootElement
    
    Dim allWindowsCond As IUIAutomationCondition
    Set allWindowsCond = winAutomation.CreateTrueCondition
    
    Dim childs As IUIAutomationElementArray
    Set childs = desktop.FindAll( _
        TreeScope_Children, _
        allWindowsCond _
    )
    
    Dim i As Long, currChild As IUIAutomationElement
    Dim catiaWindow As IUIAutomationElement
    
    For i = 0 To childs.LENGTH - 1
        Set currChild = childs.GetElement(i)
        
        If InStr(currChild.CurrentName, "CATIA V5") Then
            Set catiaWindow = currChild
            Exit For
        End If
    Next

    Dim resetPropWinCondition As IUIAutomationCondition
    Set resetPropWinCondition = winAutomation.CreatePropertyCondition( _
        UIA_NamePropertyId, _
        "プロパティをリセット" _
    )

    Dim resetPropWin As IUIAutomationElement
    Set resetPropWin = catiaWindow.FindFirst( _
        TreeScope_Children, _
        resetPropWinCondition _
    )

    Dim mainFrameWinCondition As IUIAutomationCondition
    Set mainFrameWinCondition = winAutomation.CreatePropertyCondition( _
        UIA_NamePropertyId, _
        "MainFrame" _
    )

    Dim mainFrameWin As IUIAutomationElement
    Set mainFrameWin = resetPropWin.FindFirst( _
        TreeScope_Children, _
        mainFrameWinCondition _
    )

    Dim applyToChildCondition As IUIAutomationCondition
    Set applyToChildCondition = winAutomation.CreatePropertyCondition( _
        UIA_NamePropertyId, _
        "子に適用" _
    )
    
    Dim btnApplyToChild As IUIAutomationElement
    Set btnApplyToChild = mainFrameWin.FindFirst( _
        TreeScope_Children, _
        applyToChildCondition _
    )

    Dim invokeApplyToChild As IUIAutomationInvokePattern
    Set invokeApplyToChild = btnApplyToChild.GetCurrentPattern( _
        UIA_InvokePatternId _
    )

    invokeApplyToChild.Invoke
    invokeApplyToChild.Invoke
    invokeApplyToChild.Invoke
    
    Dim btnOKCondition As IUIAutomationCondition
    Set btnOKCondition = winAutomation.CreatePropertyCondition( _
        UIA_NamePropertyId, _
        "OK" _
    )

    Dim btnOk As IUIAutomationElement
    Set btnOk = resetPropWin.FindFirst( _
        TreeScope_Children, _
        btnOKCondition _
    )
    
    Dim invokeOK As IUIAutomationInvokePattern
    Set invokeOK = btnOk.GetCurrentPattern( _
        UIA_InvokePatternId _
    )

    invokeOK.Invoke
    invokeOK.Invoke
    
    sel.Clear
End Sub

コメント一切なし、リファクタリングもしなかったのは、
結果がイマイチだったためです。

動作は、アクティブなPartの全てのボディのプロパティをリセットです。

実際に試した際の動画です。


一回目は全てのプロパティがリセット出来ていますが、二度目は不完全です。

原因はこちらです。

"子に適応"にチェックが入っているかどうかで、当然結果が変わります。
(個人的にはチェックON希望)
一回目はCATIA.StartCommandで呼び出した際はOFFでダイアログが表示され
マクロでONに切り替えています。
二回目は逆でON->OFFです。

要は"子に適応"にチェックが入っているかどうか?をチェックする方法が
分かりませんでした。
・・・取り組む前から、そうだろうとは思っていましたが。


気になる部分ですが、

    invokeApplyToChild.Invoke
    invokeApplyToChild.Invoke
    invokeApplyToChild.Invoke

は、"子に適応"にチェックを入れています。
最初は1回では無反応で、2回で入るようになる事に気が付きました。
しかし、2回でも無反応になり、3回で入るようになりました。
不安定感抜群です。(3回でもダメになるかも)

IUIAutomationElementオブジェクトには、SetFocusメソッドがありますが、
何となくフォーカスが出来ていない感じがしました。
Invokeメソッドの1回目はコントロールのフォーカスが移動している
だけの反応に感じましたが、何故3回必要なのかがわかりません。
(RefreshDisplay DoEvets等入れましたが、効果なし)

逆に"OK"ボタンに関しては、2回で大丈夫な為、1回目のinvoke
上記の記載したようにフォーカスを移動させているように感じます。

状態を判断する方法は有るのかな?・・・キャプチャ取得して画像処理?

ボディの体積をダンプする5

こちらの続きです。
ボディの体積をダンプする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でソートが必要になる場面は
それ程大きいサイズにならないと思うので、大丈夫じゃないかな?

ボディの体積をダンプする4

こちらの続きです。
ボディの体積をダンプする3 - C#ATIA

ソート関数等って恐らくVBA自体には無いですよね・・・。
ExcelのRangeであればメソッドがあるようですが、純粋なVBAには無さそう。

アルゴリズムの勉強と言う意味では、ソートを自作するのも
少しだけ意味が有りそうな気もしますが、昨今のプログラミング言語
場合は、標準で実装されていると思いますし、そこに時間を
使うのも無意味な気もするのですが・・・。


時間の確保が難しく、先人の知恵をお借りしました。

こちらは配列内の数値をソートしてダンプします。
挿入ソートはこちらからお借りして、少々変更しました。
1次元配列の並べ替え(バブルソート,挿入ソート,クイックソート)|VBAサンプル集

'vba

Option Explicit

Sub CATMain()

    Dim ary As Variant
    ary = Array(5, 10, 2, 1, 15)
    
    dump_array ary, "** ソート前 **"
    
    'ソート実行
    insertion_sort ary
    
    dump_array ary, "** ソート後 **"
    
End Sub


'https://excel-ubara.com/excelvba5/EXCELVBA228.html
Private Sub insertion_sort( _
    ByRef argAry As Variant)

    Dim low As Long
    low = LBound(argAry)
    
    Dim upp As Long
    upp = UBound(argAry)

    Dim i As Long, j As Long
    Dim vSwap As Variant
    For i = low + 1 To upp
        vSwap = argAry(i)
        For j = i - 1 To low Step -1
            If argAry(j) > vSwap Then
                argAry(j + 1) = argAry(j)
            Else
                Exit For
            End If
        Next
        argAry(j + 1) = vSwap
    Next
End Sub


Private Sub dump_array( _
    ByVal ary As Variant, _
    Optional ByVal title As String = "")

    If Len(title) > 0 Then
        dump title
    End If
    
    Dim low As Long
    low = LBound(ary)
    
    Dim upp As Long
    upp = UBound(ary)

    Dim i As Long
    For i = low To upp
        dump Str(ary(i))
    Next
End Sub


Private Sub dump( _
    ByVal msg As String)
    
    Debug.Print msg
End Sub

元々の挿入ソートが破壊的だったため、そのままにしましたが
個人的にはあまり好きじゃないですね。

実行結果はこんな感じです。

** ソート前 **
 5
 10
 2
 1
 15
** ソート後 **
 1
 2
 5
 10
 15

問題はタイトルに"ボディの体積"って書いてるくせに、ちっとも体積じゃない
って事です。

当然、ボディの配列をinsertion_sort関数に渡しても、体積でソートしてくれません。
(恐らくエラーになると思う)
insertion_sort関数内で体積を取得する処理を追記して、体積同士で比較するように
すれば体積でソート出来るでしょう。

しかし、前回Fusion360で試したように、体積ではなく表面積でソートしたい場合、
insertion_sort_by_area関数を用意する必要が出てきます。
これが、重心位置と原点の距離とか複数のマテリアルが使用されているボディ群から
重量で、等になった場合、その都度ソート関数を用意するのは面倒ですよね?

こういった場合、VBAではどうやるのが正攻法なのかな?
(その都度、ソート関数作るのが正攻法のような気はしていますが)