C#ATIA

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

クリックした面に3Dな矢印と注記を作る

こちらの続きです。
3Dな注記の位置を制御出来ているっぽい(条件付き) - C#ATIA

やっと完成しました。
注記位置については前回通り、条件付となりますが結構満足してます。

'vba sample_CreateArrow_ver0.0.3  using-'ver0.0.10'
'クリックした面に矢印を作成-注記/自動測定付き

Option Explicit

'*** 設定 ***
Private Const ArwBodyLeng = 20#     '矢印全体の長さ
Private Const ArwLeng = 5#          '矢印先端長さ
Private Const ArwAng = 30#          '矢印先端角度
Private Const TxtHeader = "t="      '注記ヘッダ
'************

Sub CATMain()
    'ドキュメントのチェック
    If Not CanExecute(Array("PartDocument", "ProductDocument")) Then Exit Sub
    
    'ユーザー選択
    Dim Elm As Variant 'SelectedElement
    Set Elm = KCL.SelectElement("面を選択", "Face")
    If KCL.IsNothing(Elm) Then Exit Sub
    
    'クリック座標取得
    Dim Pos(2) As Variant 'Double
    Call Elm.GetCoordinates(Pos)
    
    '各必要なもの取得
    Dim Pt As Part
    Set Pt = Elm.Document.Part
    
    Dim Fact As HybridShapeFactory
    Set Fact = Pt.HybridShapeFactory
    
    Dim DelList As Collection
    Set DelList = New Collection
    
    '選択面Ref
    Dim SurfRef As Reference
    Set SurfRef = Elm.Reference
    
    'クリック位置の点
    Dim PntRef As Reference
    Set PntRef = CreatePntRef(Pt, Fact, Pos)
    Call DelList.Add(PntRef)
    
    '向き確認用
    Dim Rev As Boolean: Rev = True
    Dim Msg As String: Msg = "向きを逆転しますか?"
    Dim HBdy As HybridBody
    
    Do
        '法線
        Dim LinRef As Reference
        Set LinRef = CreateNormalRef(Pt, Fact, SurfRef, PntRef, 0#, ArwBodyLeng, Rev)
        Call DelList.Add(LinRef)
        
        '線上の点
        Dim PntOnRef As Reference
        Set PntOnRef = CreatePntOnCrvRef(Pt, Fact, LinRef, ArwLeng, Rev)
        Call DelList.Add(PntOnRef)
        
        '平面
        Dim PlnRef As Reference
        Set PlnRef = CreatePlnRef(Pt, Fact, LinRef, PntRef)
        Call DelList.Add(PlnRef)
    
        '円弧
        Dim Crl As HybridShapeCircleCtrRad
        Set Crl = CreateCrl(Pt, Fact, PntOnRef, PlnRef, ArwAng, ArwLeng)
        Call DelList.Add(Crl)
        
        '円弧端点
        Dim SrtRef As Reference
        Set SrtRef = CreateEndPntRef(Pt, Fact, Crl, True)
        Call DelList.Add(SrtRef)
    
        Dim EndRef As Reference
        Set EndRef = CreateEndPntRef(Pt, Fact, Crl, False)
        Call DelList.Add(EndRef)
        
        '折れ線
        Dim PlyRef As Reference
        Set PlyRef = CreatePlyRef(Pt, Fact, SrtRef, PntRef, EndRef)
        Call DelList.Add(PlyRef)
        
        '接合
        Dim JoinRef As Reference
        Set JoinRef = CreateJoinRef(Pt, Fact, LinRef, PlyRef)
        Call DelList.Add(JoinRef)
        
        'データム化
        Dim Dtm As HybridShape
        Set Dtm = ToDatum(Pt, Fact, JoinRef)
        
        '形状セット作成
        If KCL.IsNothing(HBdy) Then
            Set HBdy = Pt.HybridBodies.Add()
        End If
        Call HBdy.AppendHybridShape(Dtm)
        
        '向き確認
        If MsgBox(Msg, vbYesNo) = vbYes Then
            Call Fact.DeleteObjectForDatum(Dtm)
            Rev = IIf(Rev, False, True)
        Else
            Exit Do
        End If
    Loop
    
    '厚み取得
    Dim Thick As Double
    Thick = TryGetThickness(Pt, Fact, SurfRef, PntRef, Rev)
    
    'ユーザー入力
    Msg = "厚みを入力してください" & vbNewLine & _
          "※注記には [ " & TxtHeader & _
          " ] が、追記されます" & vbNewLine & _
          "(空欄 又は Cancel で 中止)"
    Dim AnnoTxt As String: AnnoTxt = InputTxt(Msg, Thick)
    
    '入力値判断
    If Not AnnoTxt = vbNullString Then
        '注記作成
        Call CreateAnnotation(Pt, Fact, Dtm, Rev, TxtHeader & AnnoTxt)
        
        'ビュー非表示
        Call HideTPSView
    Else
        '中止時矢印削除
        Call RemoveItem(HBdy)
    End If
    
    'ゴミ掃除
    Dim Ref As Reference
    For Each Ref In DelList
        Call Fact.DeleteObjectForDatum(Ref)
    Next
    
    MsgBox "Done"
End Sub

'*** 注記関連 ***
'作業中止用
Private Sub RemoveItem(ByVal Itm As AnyObject)
    With CATIA.ActiveDocument.Selection
        .Clear
        .Add Itm
        .Delete
    End With
End Sub

'ユーザー入力
Private Function InputTxt( _
                    ByVal Msg As String, _
                    ByVal Thick As Double) As String
    Dim DefTxt As String
    DefTxt = IIf(Thick <= 0, vbNullString, CStr(Round(Thick, 3)))
    
    Dim Txt As String
    Do
        Txt = InputBox(Msg, , DefTxt)
        If Txt = vbNullString Then Exit Do
        If IsNumeric(Txt) Then Exit Do
        MsgBox "数字を入力して下さい!", vbOKOnly + vbExclamation
    Loop
    InputTxt = Txt
End Function

'ビュー非表示
Private Sub HideTPSView()
    With CATIA.ActiveDocument.Selection
        .Clear
        .Search "CATTPSSearch.CATTPSView,all"
        .VisProperties.SetShow catVisPropertyNoShowAttr
    End With
End Sub

'表示のUpdate
Private Sub UpdateScene(ByVal Scene As Variant)
    Dim Viewer As Viewer3D: Set Viewer = CATIA.ActiveWindow.ActiveViewer
    Dim VPnt3D As Variant 'Viewpoint3D
    Set VPnt3D = Viewer.Viewpoint3D
    
    Dim ary As Variant
    ary = KCL.GetRangeAry(Scene, 0, 2)
    Call VPnt3D.PutOrigin(ary)
    
    ary = KCL.GetRangeAry(Scene, 3, 5)
    Call VPnt3D.PutSightDirection(ary)
    
    ary = KCL.GetRangeAry(Scene, 6, 8)
    Call VPnt3D.PutUpDirection(ary)
    
    VPnt3D.FieldOfView = Scene(9)
    VPnt3D.FocusDistance = Scene(10)
    
    Call Viewer.Update
End Sub

'Viewpoint3Dからシーン取得
Private Function GetScene3D(ViewPnt3D As Viewpoint3D) As Variant
    Dim vp As Variant: Set vp = ViewPnt3D
    
    Dim origin(2) As Variant: Call vp.GetOrigin(origin)
    
    Dim sight(2) As Variant: Call vp.GetSightDirection(sight)
    GetScene3D = KCL.JoinAry(origin, sight)
    
    Dim up(2) As Variant: Call vp.GetUpDirection(up)
    GetScene3D = KCL.JoinAry(GetScene3D, up)
    
    Dim FieldOfView(0) As Variant: FieldOfView(0) = vp.FieldOfView
    GetScene3D = KCL.JoinAry(GetScene3D, FieldOfView)
    
    Dim FocusDist(0) As Variant: FocusDist(0) = vp.FocusDistance
    GetScene3D = KCL.JoinAry(GetScene3D, FocusDist)
End Function

'現状の視点取得
Private Function GetViewPnt3D() As Viewpoint3D
    Set GetViewPnt3D = CATIA.ActiveWindow.ActiveViewer.Viewpoint3D
End Function

'注記
Private Sub CreateAnnotation( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal Arw As HybridShape, _
                    ByVal Rev As Boolean, _
                    ByVal Txt As String)
    '矢印端点座標取得 結構ギャンブルw
    Dim Idx As Long: Idx = IIf(Rev, 4, 3)
    Dim sel As Selection: Set sel = Pt.Parent.Selection
    Dim SelName As String
    With sel
        .Clear
        .Add Arw
        .Search "Topology.CGMVertex,sel"
        SelName = .Item2(Idx).Reference.Name
        .Clear
    End With
    
    Dim PntRef As Reference
    Set PntRef = Pt.CreateReferenceFromBRepName(KCL.GetBrepName(SelName), Arw)
    Dim Pnt As Variant 'HybridShapePointExplicit
    
    Set Pnt = Fact.AddNewPointDatum(PntRef)
    Dim Pos(2) As Variant 'Double
    Call Pnt.GetCoordinates(Pos)
    Call Fact.DeleteObjectForDatum(Pnt)
    
    'カメラ情報取得
    Dim NowVw As Variant
    NowVw = GetScene3D(GetViewPnt3D)
    
    'カメラ一時切り替え
    Call UpdateScene(Array(0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0))

    '注記
    Dim USurf As UserSurface
    Set USurf = Pt.UserSurfaces.Generate(PntRef)

    Dim ATSet As AnnotationSet
    Set ATSet = Pt.AnnotationSets.Add("")
    
    Dim Anno As Annotation
    Set Anno = ATSet.AnnotationFactory.CreateEvoluateText(USurf, Pos(0), Pos(1), Pos(2), False)
    Anno.Text.Text = Txt
    Call Pt.UpdateObject(Anno)
    
    'カメラ戻し
    Call UpdateScene(NowVw)
End Sub

'*** 厚み関連 ***
'ボディ面の時のみ、厚みの取得に挑戦
Private Function TryGetThickness( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal SurfRef As Reference, _
                    ByVal PntRef As Reference, _
                    ByVal Rev As Boolean) As Double
    TryGetThickness = -1#
    
    '親ボディ取得
    Dim PBody As Body: Set PBody = KCL.GetParent_Of_T(SurfRef, "Body")
    If KCL.IsNothing(PBody) Then Exit Function
    
    '履歴最後
    Dim Shps As Shapes: Set Shps = PBody.Shapes
    Dim ShpRef As Reference: Set ShpRef = Pt.CreateReferenceFromGeometry(Shps.Item(Shps.Count))
    
    'ゴミ箱
    Dim DelList As Collection: Set DelList = New Collection
    
    '法線逆向き
    Rev = IIf(Rev, False, True)
    Dim LinRef As Reference
    Set LinRef = CreateNormalRef(Pt, Fact, SurfRef, PntRef, 0.002, 20000000#, Rev)
    Call DelList.Add(LinRef)
    
    '交差
    Dim InterRef As Reference
    Set InterRef = GetIntersectRef(Pt, Fact, ShpRef, LinRef)
    If KCL.IsNothing(InterRef) Then GoTo ExitGetThickness
    Call DelList.Add(InterRef)
    
    '近傍
    Dim NearRef As Reference
    Set NearRef = GetNearRef(Pt, Fact, InterRef, PntRef)
    Call DelList.Add(NearRef)
    
    '最短距離
    TryGetThickness = GetMaximumLength(Pt, PntRef, NearRef)
    
ExitGetThickness:

    'ゴミ掃除
    Dim Ref As Reference
    For Each Ref In DelList
        Call Fact.DeleteObjectForDatum(Ref)
    Next
End Function

'最短距離測定
Private Function GetMaximumLength(ByVal Pt As Part, _
                                  ByVal Ref1 As Reference, _
                                  ByVal Ref2 As Reference) As Double
    GetMaximumLength = Pt.Parent.GetWorkbench("SPAWorkbench") _
                         .GetMeasurable(Ref1) _
                         .GetMinimumDistance(Ref2)
End Function

'近傍
Private Function GetNearRef( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal InterRef As Reference, _
                    ByVal PntRef As Reference) As Reference
    Dim Near As HybridShapeNear
    Set Near = Fact.AddNewNear(InterRef, PntRef)
    Call Pt.UpdateObject(Near)

    Set GetNearRef = Pt.CreateReferenceFromObject(Near)
End Function

'交差
Private Function GetIntersectRef( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal ShpRef As Reference, _
                    ByVal LinRef As Reference) As Reference
    Set GetIntersectRef = Nothing
    
    Dim Inter As HybridShapeIntersection
    Set Inter = Fact.AddNewIntersection(ShpRef, LinRef)
    Inter.PointType = 0
    
    On Error Resume Next
        Call Pt.UpdateObject(Inter)
        If Not Err.Number = 0 Then
            Err.Number = 0
            Call Fact.DeleteObjectForDatum(Inter)
            Exit Function
        End If
    On Error GoTo 0
    
    Set GetIntersectRef = Pt.CreateReferenceFromObject(Inter)
End Function

'*** 矢印関連 ***
'接合
Private Function CreateJoinRef( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal LinRef As Reference, _
                    ByVal PlyRef As Reference) As Reference
    Dim Join As HybridShapeAssemble
    Set Join = Fact.AddNewJoin(LinRef, PlyRef)
    With Join
        .SetConnex 1
        .SetManifold 0
        .SetSimplify 0
        .SetSuppressMode 0
        .SetDeviation 0.001
        .SetAngularToleranceMode 0
        .SetAngularTolerance 0.5
        .SetFederationPropagation 0
    End With
    Call Pt.UpdateObject(Join)
    
    Set CreateJoinRef = Pt.CreateReferenceFromObject(Join)
End Function

'折れ線
Private Function CreatePlyRef( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal Ref1 As Reference, _
                    ByVal Ref2 As Reference, _
                    ByVal Ref3 As Reference) As Reference
    Dim Ply As HybridShapePolyline
    Set Ply = Fact.AddNewPolyline()
    With Ply
        .InsertElement Ref1, 1
        .InsertElement Ref2, 2
        .InsertElement Ref3, 3
        .Closure = False
    End With
    Call Pt.UpdateObject(Ply)
    
    Set CreatePlyRef = Pt.CreateReferenceFromObject(Ply)
End Function

'端点
Private Function CreateEndPntRef( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal Crl As HybridShapeCircleCtrRad, _
                    ByVal StartEndFg As Boolean) As Reference
    Dim Cnt(2) As Variant
    Call Fact.GSMGetObjectFromReference(Crl.Center).GetCoordinates(Cnt)
    
    Dim R As Double
    R = Crl.Radius.Value
    
    Dim Pos(2) As Variant 'Double
    Dim Direct As HybridShapeDirection
    Set Direct = Crl.FirstDirection
    Pos(0) = Direct.GetXVal
    Pos(1) = Direct.GetYVal
    Pos(2) = Direct.GetZVal
    
    Dim i As Long
    For i = 0 To UBound(Pos)
        Pos(i) = (Pos(i) * R * IIf(StartEndFg, 1, -1)) + Cnt(i)
    Next
    
    Set CreateEndPntRef = CreatePntRef(Pt, Fact, Pos)
End Function

'円弧
Private Function CreateCrl( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal PntRef As Reference, _
                    ByVal PlnRef As Reference, _
                    ByVal Ang As Double, _
                    ByVal Leng As Double) As HybridShapeCircleCtrRad
    Dim PAI As Double: PAI = 4 * Atn(1)
    Dim R As Double: R = Tan((Ang * 0.5) * PAI / 180) * Leng
    Dim Crl As HybridShapeCircleCtrRad
    Set Crl = Fact.AddNewCircleCtrRadWithAngles(PntRef, PlnRef, False, R, 0#, 180#)
    Call Pt.UpdateObject(Crl)
    
    Set CreateCrl = Crl
End Function

'平面
Private Function CreatePlnRef( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal CrvRef As Reference, _
                    ByVal PntRef As Reference) As Reference
    Dim Pln As HybridShapePlaneNormal
    Set Pln = Fact.AddNewPlaneNormal(CrvRef, PntRef)
    Call Pt.UpdateObject(Pln)
    
    Set CreatePlnRef = Pt.CreateReferenceFromObject(Pln)
End Function

'点-線上
Private Function CreatePntOnCrvRef( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal CrvRef As Reference, _
                    ByVal Leng As Double, _
                    ByVal Rev As Boolean) As Reference
    Dim Pnt As HybridShapePointOnCurve
    Set Pnt = Fact.AddNewPointOnCurveFromDistance(CrvRef, Leng, Rev)
    Pnt.DistanceType = 1
    Call Pt.UpdateObject(Pnt)
    
    Set CreatePntOnCrvRef = Pt.CreateReferenceFromObject(Pnt)
End Function

'データム化
Private Function ToDatum( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal Ref As Reference) As HybridShape
    Dim Dtm As HybridShape ' HybridShapeCurveExplicit
    Set Dtm = Fact.AddNewCurveDatum(Ref)
    Call Pt.UpdateObject(Dtm)
    
    Set ToDatum = Dtm
End Function

'点
Private Function CreatePntRef( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal ary As Variant) As Reference
    Dim Pnt As HybridShapePointCoord
    Set Pnt = Fact.AddNewPointCoord(ary(0), ary(1), ary(2))
    Call Pt.UpdateObject(Pnt)
    
    Set CreatePntRef = Pt.CreateReferenceFromObject(Pnt)
End Function

'法線
Private Function CreateNormalRef( _
                    ByVal Pt As Part, _
                    ByVal Fact As HybridShapeFactory, _
                    ByVal SurfRef As Reference, _
                    ByVal PntRef As Reference, _
                    ByVal SLng As Double, _
                    ByVal ELng As Double, _
                    ByVal Rev As Boolean) As Reference
    Dim Lin As HybridShapeLineNormal
    Set Lin = Fact.AddNewLineNormal(SurfRef, PntRef, SLng, ELng, Rev)
    Call Pt.UpdateObject(Lin)
    Set CreateNormalRef = Pt.CreateReferenceFromObject(Lin)
End Function

かなり長めのコードになってしまいました。
実は、昨日の時点でほぼ出来上がっていたのですが、少し機能を
追加することにしました。

実行した際は、このような感じになります。

最初の動作はサーフェスをクリックした場合です。
2回目の操作はボディをクリックし、向きが相応しくない場合です。
3回目以降はボディをクリックし、意図した向きに矢印を作成した場合です。

3回目以降は、注記文字を入力する為のダイアログのデフォルトとして
数値が入っています。 これは自動で厚みを測定しています。
但し、条件によっては正しくない値が表示される為、完全ではない事を
考慮しておいてください。
(アホみたいな桁数にならないように、小数点以下3桁にしています)

・・・結構、ツールとして使えるかも。