C#ATIA

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

2Dの曲線を選択した際、近い側の端点を取得

「2Dの曲線を選択した際、近い側の端点を取得したい」との相談を
頂きました。

通常要素の選択であれば、Selection.SelectElement2が殆どなのですが、
今回の場合は、Selection.IndicateOrSelectElement2Dを使用します。
理由は単純で、クリックした位置の座標を取得する為です。

クリックした位置さえ取得出来れば、後は2つの端点との距離を比較し
短い方が近い端点と判断出来ます。

'vba 2Dの線を選択した際、近い側の端点を取得
Option Explicit

Sub CATMain()

    Dim doc As DrawingDocument
    Set doc = CATIA.ActiveDocument
    
    Dim vi As DrawingView
    Set vi = doc.Sheets.ActiveSheet.views.ActiveView
    
    Do
        '選択後は配列が返ってきて
        'crv_near(0)-選択した線
        'crv_near(1)-近い側の端点(Point2D)
        Dim crv_near As Variant
        crv_near = GetNearPoint()
        
        If IsEmpty(crv_near) Then
            Exit Do
        End If
        Call initCircle(crv_near(1))
    Loop
    
    vi.Activate
    
    MsgBox "Done"
    
End Sub

'*** 2D線を選択 - 選択した曲線と近い側の端点取得 ***
'return array(Curve2D,Point2D)
Private Function GetNearPoint() As Variant

    Dim doc As DrawingDocument
    Set doc = CATIA.ActiveDocument
    
    Dim sel As Variant ' Selection
    Set sel = doc.selection
    sel.Clear
    
    '座標選択
    Dim status As String
    Dim ObjectSelected
    Dim WindowLocation(1)
    Dim filter As Variant
    filter = Array("Curve2D")
    
    'WindowLocationの座標値はアクティブビューに対しての座標値
    status = "MouseMove"
    Dim pos As Variant
    Do While (status = "MouseMove")
        status = sel.IndicateOrSelectElement2D( _
            "線を選択/キャンセル ESC", _
            filter, _
            False, _
            False, _
            True, _
            ObjectSelected, _
            WindowLocation)
        If Not ObjectSelected Then
            pos = WindowLocation
        End If
    Loop
    
    'ESCキー
    If sel.Count2 < 1 Then
        Exit Function
    End If
    
    '選択した線
    Dim crv As Curve2D
    Set crv = sel.Item(1).Value
    
    '*** クリック位置の座標 ***
    'アクティブビュー
    Dim ac As DrawingView
    Set ac = doc.Sheets.ActiveSheet.views.ActiveView
    
    'ターゲットビュー
    Dim tg As DrawingView
    Set tg = crv.Parent.Parent

    'ビュー間ベクトル
    Dim tran_vec As Variant
    tran_vec = GetTransVec(ac, tg)
    
    '該当するビューの座標に変換
    pos = Add2d(tran_vec, pos)
    
    '*** クリック位置と端点比較 ***
    'crvのVariant
    Dim crvVri As Variant
    Set crvVri = crv
    
    '始点座標値
    Dim pos1(1) As Variant
    Call crvVri.StartPoint.GetCoordinates(pos1)
    
    '終点座標値
    Dim pos2(1) As Variant
    Call crvVri.EndPoint.GetCoordinates(pos2)
    
    '距離比較
    Dim res As Variant
    If LengSqr(pos, pos1) > LengSqr(pos, pos2) Then
        res = Array(crv, crv.EndPoint)
    Else
        res = Array(crv, crv.StartPoint)
    End If
    
    GetNearPoint = res
    
End Function

'ビュー間ベクトル
Private Function GetTransVec( _
    ByVal act_v As DrawingView, _
    ByVal tgt_v As DrawingView) As Variant
    
    GetTransVec = Sub2d( _
        Array(act_v.xAxisData, act_v.yAxisData), _
        Array(tgt_v.xAxisData, tgt_v.yAxisData))
End Function

'和2D
Private Function Add2d( _
    ByVal V1 As Variant, _
    ByVal V2 As Variant) As Variant
    
    Add2d = Array(V1(0) + V2(0), V1(1) + V2(1))
End Function

'差2D
Private Function Sub2d( _
    ByVal V1 As Variant, _
    ByVal V2 As Variant) As Variant
    
    Sub2d = Array(V1(0) - V2(0), V1(1) - V2(1))
End Function

'2点距離の平方数
Private Function LengSqr( _
    ByVal p1 As Variant, _
    ByVal p2 As Variant) As Double
    
    Dim A#: A = p2(0) - p1(0)
    Dim B#: B = p2(1) - p1(1)
    LengSqr = A * A + B * B
End Function

'***************

'確認用 点を中心に円を描く
Private Sub initCircle( _
    ByVal pnt As Point2D)
    
    Dim vi As DrawingView
    Set vi = pnt.Parent.Parent
    vi.Activate 'アクティブにする必要有り
    
    Dim fact As Factory2D
    Set fact = vi.Factory2D
    
    Dim pntVri As Variant
    Set pntVri = pnt
    
    Dim pos(1)
    Call pntVri.GetCoordinates(pos)
    
    Dim crl As Circle2D
    Set crl = fact.CreateClosedCircle(pos(0), pos(1), 5#)
    
    crl.CenterPoint = pnt

End Sub

LengSqr関数(今見るとイマイチな名称です…)ですが、配列を点座標と
想定し2点間距離の平方数を返します。

この方法は、C# OpenGLプラットフォーム「ヒスイ」で学びました。
コンピューターは人に比べれば計算は高速ですが、全て同一速度で
処理されるわけではないのも事実です。平方根の演算も時間のかかる
(コストの高い)処理らしいです。

2点間の正しい距離を演算するためには、平方根の演算が必要となり
ますが(平方根を避けた近似演算式があるようですが、知りません)
今回のように「どちらの点が近いのかな?」と言う処理に関して言えば
平方根を演算しなくても、誤った結果にはならないです。
(結果が逆転する事は無い)

とは言え、「この程度の処理にそこまで気にする必要あるの?」とも
受け取れるのですが・・・。


余談ですが、「ヒスイ」の公開が無くなっていたんですね。
株式会社カタッチ
開発された方は、既に退社されているので仕方ないのは確かですが。