C#ATIA

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

マウスカーソルの座標値を取得する4

こちらの続きです。
マウスカーソルの座標値を取得する3 - C#ATIA
3D空間でマウスの位置が取得できるようになったので、マウスの位置で
曲率と勾配角度を動的に取得したいです。そう、CATIAのオンザフライの機能を
実現したいと細々とやってます。
テストモデルはこのような感じで、勾配解析を行った状態です。
f:id:kandennti:20180606104408p:plain
勾配角度はZ軸(青い線)方向とします。紫色は垂直面で、青緑?色が水平面です。
最小R(曲率の逆数)は無事に取得できているようなのですが、
勾配が上手く取得出来ていません。

    app = adsk.core.Application.get()
    des = adsk.fusion.Design.cast(app.activeProduct)
    comp = des.rootComponent
    
    #pnt - サーフェス上の任意の点
    #maxTangent - サーフェス上の任意の点の法線3Dベクトル
    axisZ = comp.zConstructionAxis.geometry
    tang = adsk.core.InfiniteLine3D.create(pnt, maxTangent)
    measMng = app.measureManager
    ang = measMng.measureAngle(axisZ, tang).value

比較的最近実装されたmeasureManagerオブジェクトを利用し、
2つの無限直線の角度を求めています。
結果はこちら
f:id:kandennti:20180606104420p:plain
左が水平面で右が垂直面です。 両方90°です…。
R面を測定していても水平に近い部分で90°弱になり、
垂直に近い部分で0°強になる為、個人的な直感とは逆です。
又、アンダーカットとなる部分ではマイナスの値で返ってきて
欲しいのですが、全てプラスです。(atan関数なんだろうなぁ)

よく見たらVecter3Dオブジェクトに2つのベクトル間の角度を求める
メソッドがあった為、コードを修正。

    ・・・
    vecZ = comp.zConstructionAxis.geometry.direction
    ang = vecZ.angleTo(maxTangent)

こちらの方が、はるかにコードが短かったです。
でも結果は同じです。

真面目に計算しなきゃなら無そうなので、悩み中です。

ThickOnTheFly2

こちらの続き? です。
ThickOnTheFly1 - C#ATIA
こちら作りかけて止めました。続けてもいないのですがコードを
記載していなかったので・・・・。
正直な所、変数・関数名等が酷いのですが、直す気力がありません。

'vba using-'KCL0.0.12'
'動的にボディの厚みをStatusBarに表示させる
'厚み解析のアルゴリズム自体が正しくないです

Private Const MaxLng = 2000000#
Private Const MinLng = 0.002

Sub CATMain()
    Dim Sel 'As Selection
    Set Sel = CATIA.ActiveDocument.Selection
    Dim Pt As Part
    Set Pt = CATIA.ActiveDocument.Part
    Dim PlaneXY As AnyObject
    Set PlaneXY = Pt.OriginElements.PlaneXY
    Dim fact As HybridShapeFactory
    Set fact = Pt.HybridShapeFactory
    
    Dim Status 'As Long
    Dim WindowLocation2D(1)
    Dim WindowLocation3D(2)
    Dim ObjectSelected
    
    Dim Hb As HybridBody
    Set Hb = Pt.hybridBodies.Add
    
    'パーツボディ
    Dim Bd As Body
    
    Set Bd = KCL.SelectItem("body", "Body")
    
    'パーツボディの最後のパートデザインフューチャー
    Dim LastShape As Shape
    Set LastShape = Bd.Shapes.Item(Bd.Shapes.Count)
    
    'フューチャーのリファレンス
    Dim RefShape As Reference
    Set RefShape = Pt.CreateReferenceFromGeometry(LastShape)
    
    Dim ShapeSrf As HybridShape: Set ShapeSrf = CreateExtract(Pt, fact, RefShape)
    Dim RefShapeSrf As Reference: Set RefShapeSrf = Pt.CreateReferenceFromGeometry(ShapeSrf)
    
    'カーソルがボディ上?
    Dim OnBdyLst As Collection: Set OnBdyLst = New Collection
    
    '原点
    Dim ori As HybridShape: Set ori = CreatePnt(Pt, fact, Array(0#, 0#, 0#))
    Call Pt.UpdateObject(ori)
    Dim RefOri As Reference: Set RefOri = Pt.CreateReferenceFromGeometry(ori)
    
    Dim PntCam As HybridShape: Set PntCam = CreatePnt(Pt, fact, Array(0#, 0#, 0#))
    Dim RefCam As Reference: Set RefCam = Pt.CreateReferenceFromGeometry(PntCam)
    
    'マウスカーソル3D位置
    Dim PntTgt As HybridShape: Set PntTgt = CreatePnt(Pt, fact, Array(0#, 0#, 1#))
    Dim RefTgt As Reference: Set RefTgt = Pt.CreateReferenceFromGeometry(PntTgt)
    
    'カメラ方向単位ベクトル
    Dim CamUnitVec As HybridShape: Set CamUnitVec = CreatePnt(Pt, fact, Array(0#, 0#, 1#))
    Dim RefCamUnitVec As Reference: Set RefCamUnitVec = Pt.CreateReferenceFromGeometry(CamUnitVec)
    
    'カメラ方向用単位線
    Dim UnitLin As HybridShape: Set UnitLin = CreateLine(Pt, fact, RefOri, RefCamUnitVec, 0#, 0#)
    Dim RefUnitLin As Reference: Set RefUnitLin = Pt.CreateReferenceFromGeometry(UnitLin)
    Call OnBdyLst.Add(UnitLin)
    
    'カメラ方向
    Dim CamVec As HybridShape: Set CamVec = CreateDirection(fact, RefUnitLin)
    Dim RefCamVec As Reference: Set RefCamVec = Pt.CreateReferenceFromGeometry(CamVec)
    Call OnBdyLst.Add(CamVec)
    
    Dim LinVew As HybridShape: Set LinVew = CreateExtrude(fact, RefTgt, CamVec, MaxLng, MaxLng)
    Dim RefVew As Reference: Set RefVew = Pt.CreateReferenceFromGeometry(LinVew)
    Call OnBdyLst.Add(LinVew)
    
    Dim Isc As HybridShape: Set Isc = GetIntersect(fact, RefShapeSrf, RefVew)
    Dim RefIsc As Reference: Set RefIsc = Pt.CreateReferenceFromGeometry(Isc)
    Call OnBdyLst.Add(Isc)
    
    Dim Near As HybridShape: Set Near = GetNear(fact, RefIsc, RefCam)
    Dim RefNer As Reference: Set RefNer = Pt.CreateReferenceFromGeometry(Near)
    Call OnBdyLst.Add(Near)
    Hb.AppendHybridShape Near: Call SetColor(Near)
    
    Dim Rev As Boolean: Rev = True 'これいらないかも
    Dim Nml As HybridShape: Set Nml = CreateNormal(fact, RefShapeSrf, RefNer, MinLng, MaxLng, Rev)
    Dim RefNml As Reference: Set RefNml = Pt.CreateReferenceFromGeometry(Nml)
    Call OnBdyLst.Add(Nml)
    
    'Nml.Pointエラー対策
    Dim NmlPnt As HybridShapePointOnCurve: Set NmlPnt = CreatePntOnCrv(fact, RefNml, 0#, Rev) '***********
    Dim RefNmlPnt As Reference: Set RefNmlPnt = Pt.CreateReferenceFromGeometry(NmlPnt)
    Call OnBdyLst.Add(NmlPnt)
    
    '厚さリスト
    Dim ThickLst As Collection: Set ThickLst = New Collection
    
    Dim ThkIsc As HybridShape: Set ThkIsc = GetIntersect(fact, RefShapeSrf, RefNml)
    Dim RefThkIsc As Reference: Set RefThkIsc = Pt.CreateReferenceFromGeometry(ThkIsc)
    Call ThickLst.Add(ThkIsc)
    
    Dim ThkPnt As HybridShape: Set ThkPnt = GetNear(fact, RefThkIsc, RefNer)
    Dim RefThkPnt As Reference: Set RefThkPnt = Pt.CreateReferenceFromGeometry(ThkPnt)
    Call ThickLst.Add(ThkPnt)
    Hb.AppendHybridShape ThkPnt: Call SetColor(ThkPnt)
    
    Dim vp As Variant ' Viewpoint3D
    Set vp = GetViewPnt3D
    Dim origin(2) As Variant:
    Dim sight(2) As Variant
    Dim Thi$: Thi = "0"
    
    Do
        Status = Sel.IndicateOrSelectElement3D(PlaneXY, "t=" & Thi, Array("HybridShapePointCoord"), True, True, True, ObjectSelected, WindowLocation2D, WindowLocation3D)

        Select Case Status
            Case "Cancel"
                Exit Do
            Case "Normal"
            
            Case "MouseMove"
                Call vp.GetOrigin(origin)
                Call UpdatePnt(Pt, PntCam, origin)
                Call UpdatePnt(Pt, PntTgt, WindowLocation3D)
                Call vp.GetSightDirection(sight)
                Call UpdatePnt(Pt, CamUnitVec, sight)
                If TryUpdate(Pt, OnBdyLst) Then
                    Debug.Print GetMinLng(Pt, RefShape, RefNmlPnt)
                    If Not GetMinLng(Pt, RefShape, RefNmlPnt) = 0 Then
                        'Rev
                        Nml.Orientation = IIf(Nml.Orientation = 1, -1, 1)
                        Call Pt.UpdateObject(Nml)
                        NmlPnt.Orientation = Nml.Orientation
                        Call Pt.UpdateObject(NmlPnt)
                    End If
                    If TryUpdate(Pt, ThickLst) Then
                        Thi = CStr(GetMinLng(Pt, RefNer, RefThkPnt))
                    End If
                End If
        End Select
    Loop
End Sub

Private Sub SetColor(ByVal Shp As AnyObject)
    With CATIA.ActiveDocument.Selection
        .Clear
        .Add Shp
        .VisProperties.SetRealColor 0, 255, 0, 1
        .VisProperties.SetSymbolType 4
        .Clear
    End With
End Sub

Private Function CreateExtrude( _
                    ByVal fact As HybridShapeFactory, _
                    ByVal Ref As Reference, _
                    ByVal Dir As HybridShapeDirection, _
                    ByVal StLng As Double, _
                    ByVal EdLng As Double) As HybridShape
    Set CreateExtrude = fact.AddNewExtrude(Ref, StLng, EdLng, Dir)
End Function

Private Function CreateDirection( _
                    ByVal fact As HybridShapeFactory, _
                    ByVal Ref As Reference) As HybridShape
    Set CreateDirection = fact.AddNewDirection(Ref)
End Function

Private Function TryUpdate( _
                    ByVal Pt As Part, _
                    ByRef LstObj As Collection) As Boolean
    TryUpdate = False
    Dim Shp As HybridShape
    On Error Resume Next
        For Each Shp In LstObj
            Call Pt.UpdateObject(Shp)
            If Not Err.Number = 0 Then
                Err.Number = 0
                Exit Function
            End If
        Next
    On Error GoTo 0
    TryUpdate = True
End Function

'点-線上
Private Function CreatePntOnCrv( _
                    ByVal fact As HybridShapeFactory, _
                    ByVal CrvRef As Reference, _
                    ByVal Lng As Double, _
                    ByVal Rev As Boolean) As Reference
    Dim Pnt As HybridShapePointOnCurve
    Set Pnt = fact.AddNewPointOnCurveFromDistance(CrvRef, Lng, Rev)
    
    Set CreatePntOnCrv = Pnt
End Function

'法線
Private Function CreateNormal( _
                    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)
    Set CreateNormal = Lin
End Function

'抽出
Private Function CreateExtract( _
                    ByVal Pt As Part, _
                    ByVal fact As HybridShapeFactory, _
                    ByVal Ref As Reference) As HybridShape
    Dim HSExt As HybridShapeExtract
    Set HSExt = fact.AddNewExtract(Ref)
    With HSExt
        .PropagationType = 3
        .ComplementaryExtract = False
        .IsFederated = False
    End With
    Call Pt.UpdateObject(HSExt)
    Set CreateExtract = HSExt
End Function

'近傍
Private Function GetNear( _
                    ByVal fact As HybridShapeFactory, _
                    ByVal InterRef As Reference, _
                    ByVal pntRef As Reference) ' As Reference
    Dim Near As HybridShapeNear
    Set Near = fact.AddNewNear(InterRef, pntRef)

    Set GetNear = Near
End Function

'交差
Private Function GetIntersect( _
                    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
    
    Set GetIntersect = Inter
End Function

'最短距離測定
Private Function GetMinLng( _
                    ByVal Pt As Part, _
                    ByVal Ref1 As Reference, _
                    ByVal Ref2 As Reference) As Double
    GetMinLng = MaxLng '-1
    On Error Resume Next
        GetMinLng = Pt.Parent.GetWorkbench("SPAWorkbench") _
                         .GetMeasurable(Ref1) _
                         .GetMinimumDistance(Ref2)
    On Error GoTo 0
End Function

Private Sub UpdatePnt( _
                    ByVal Pt As Part, _
                    ByRef Pnt As Variant, _
                    ByVal Pos As Variant)
    Call Pnt.SetCoordinates(Pos)
    Call Pt.UpdateObject(Pnt)
End Sub

'点
Private Function CreatePnt( _
                    ByVal Pt As Part, _
                    ByVal fact As HybridShapeFactory, _
                    ByVal Ary As Variant) As HybridShape
    Dim Pnt As HybridShapePointCoord
    Set Pnt = fact.AddNewPointCoord(Ary(0), Ary(1), Ary(2))
    Call Pt.UpdateObject(Pnt)
    Set CreatePnt = Pnt
End Function

Private Function CreateLine( _
                    ByVal Pt As Part, _
                    ByVal fact As HybridShapeFactory, _
                    ByVal Ref1 As Reference, _
                    ByVal Ref2 As Reference, _
                    ByVal StLng As Double, _
                    ByVal EdLng As Double) As HybridShape
    Dim Lin As HybridShapeLinePtPt
    Set Lin = fact.AddNewLinePtPtExtended(Ref1, Ref2, StLng, EdLng)
    Call Pt.UpdateObject(Lin)
    Set CreateLine = Lin
End Function

'表示の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

確か、一度XY平面を通過する位置にマウスを移動しないと、厚みが表示されなかった
記憶です。(奥行きの値を取得する為です)

最初に必要となるGSDの要素をHybridShapeFactoryで作り出し、
マウスを動かす度にUpdateしまくり、厚みを表示させています。
そのため、チラツキ感が酷いです。

「厚みを表示」と書いておりますが、算出方法が(少なくとも)2種類あり
こちらのサイトの "RAY法" になるのだと思います。
GeomCaliper | ジオムキャリパー | CATIA V5モデルの肉厚の測定と表示 | アイコクアルファ株式会社
本当は "Sphere法" を行いたいのですが、アルゴリズムが良くわからないんです。
(測定対象となる相手の面さえ見つければ良いのですが、効率良く見つけ出す方法が
 わかりません)


・・・先日知ったのですが、海外の方もここを見て頂いているようでして、
3年ぐらい続けるとそんな事もあるんですね。

マウスカーソルの座標値を取得する3

こちらの続きです。
マウスカーソルの座標値を取得する2 - C#ATIA

前回はマウスカーソルの3D位置は正しい値が取得できていませんでしたが、
今回は正しい値が取得出来ているはずです。

#FusionAPI_python
#Author-kantoku
#Description-MouseMoveTest ver0.0.3

import adsk.core, adsk.fusion, traceback

_ui  = None
_handlers = []
_faces = []
_covunit = 0

#このコマンドのイベント・ダイアログ作成
class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            global _handlers
            cmd = adsk.core.Command.cast(args.command)

            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onMouseMove = MyMouseMoveHandler()
            cmd.mouseMove.add(onMouseMove)
            _handlers.append(onMouseMove)

            inputs = cmd.commandInputs
            
            inputs.addTextBoxCommandInput('Vp_Pos', 'Fusion360画面上の座標値', '-', 1, True)
            inputs.addTextBoxCommandInput('Sc_Pos', 'ディスプレイ上の座標値', '-', 1, True)
            inputs.addTextBoxCommandInput('3D_Pos', 'マウス3D仮座標値', '-', 1, True)
            inputs.addTextBoxCommandInput('Cam_Vec', 'カメラ向き(単位ベクトル)', '-', 1, True)
            inputs.addTextBoxCommandInput('Hit', 'マウス3D座標値', 'Non!', 1, True)
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
            
#このコマンドの破棄
class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            adsk.terminate()
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

#MouseMoveイベント
class MyMouseMoveHandler(adsk.core.MouseEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.MouseEventArgs.cast(args)
        cmd = eventArgs.firingEvent.sender
        inputs = cmd.commandInputs
        
        #ビューポイント
        vp = eventArgs.viewport
        
        #Fusion360画面上の座標値
        vppos = eventArgs.viewportPosition
        ui_vp = inputs.itemById('Vp_Pos')
        ui_vp.text = 'x:[%d] y:[%d]'%(vppos.x, vppos.y)
        
        #ディスプレイ上の座標値
        scrpos = vp.viewToScreen(vppos)
        ui_sc = inputs.itemById('Sc_Pos')
        ui_sc.text = 'x:[%d] y:[%d]'%(scrpos.x, scrpos.y)
        
        #マウス3D仮座標値-奥行きがNGな為正しくない(通過点)
        d3pos = vp.viewToModelSpace(vppos)
        d3_sc = inputs.itemById('3D_Pos')
        d3_sc.text = 'x:{:.3f} y:{:.3f} z:{:.3f}'.format(
            d3pos.x * _covunit, 
            d3pos.y * _covunit, 
            d3pos.z * _covunit)
        
        #カメラ向き(単位ベクトル)
        cam = vp.camera
        vec = cam.eye.vectorTo(cam.target)
        vec.normalize()
        vec_sc = inputs.itemById('Cam_Vec')
        vec_sc.text = 'x:{:.3f} y:{:.3f} z:{:.3f}'.format(vec.x, vec.y, vec.z)

        #マウス3D座標値
        hit_sc = inputs.itemById('Hit')
        onface = OnFace(vp, eventArgs.viewportPosition)
        if onface == None:
            hit_sc.text = 'Non!'
        else:
            hit_sc.text = 'x:{:.3f} y:{:.3f} z:{:.3f}'.format(
                onface[1].x * _covunit,
                onface[1].y * _covunit,
                onface[1].z * _covunit)

#マウスカーソルの3D取得
def OnFace(vp, vp_pos):
    d3pos = vp.viewToModelSpace(vp_pos)
    cam = vp.camera
    vec = cam.eye.vectorTo(cam.target)
    
    pnt = adsk.core.Point3D.create(
        d3pos.x + vec.x, 
        d3pos.y + vec.y, 
        d3pos.z + vec.z)
        
    mouse3d = adsk.core.Line3D.create(d3pos, pnt).asInfiniteLine()    
    
    ints = [(face, mouse3d.intersectWithSurface(geo))
            for (face, geo) in _faces if mouse3d.intersectWithSurface(geo).count > 0]
    
    ints = [(face, p) 
            for (face, pnts) in ints
            for p in pnts]

    ints = [(face, p, face.evaluator.getParameterAtPoint(p))
            for (face, p) in ints]
    
    ints = [(face, p, prm)
            for (face, p, (res, prm)) in ints if res]
    
    ints = [(face, p, prm)
            for (face, p, prm) in ints]
            
    ints = [(face, p, cam.eye.distanceTo(p))
            for (face, p, prm) in ints if face.evaluator.isParameterOnFace(prm)]
    
    if len(ints) < 1:
        return None
    
    return min(ints, key = (lambda x: x[2]))
    
def run(context):
    try:
        app = adsk.core.Application.get()
        _ui = app.userInterface
                
        cmdDef = _ui.commandDefinitions.itemById('Mouse_Move_Test')
        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                'Mouse_Move_Test', 
                'Mouse_Move_Test', 
                'Mouse_Move_Test')

        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)
        
        des = adsk.fusion.Design.cast(app.activeProduct)
        
        #全サーフェス取得
        global _faces
        _faces = [(face, face.geometry)
            for comp in des.allComponents if comp.isBodiesFolderLightBulbOn
            for bBody in comp.bRepBodies if bBody.isLightBulbOn & bBody.isVisible
            for face in bBody.faces]

        #単位準備
        global _covunit
        unitsMgr = des.unitsManager
        defLenUnit = unitsMgr.defaultLengthUnits
        _covunit = unitsMgr.convert(1, unitsMgr.internalUnits, defLenUnit)


        cmdDef.execute()

        adsk.autoTerminate(False)
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

コードが汚い。特にOnFace関数。
スプリクト実行後、ダイアログが出ますが一番下の項目が正確であろう3D座標値です。
マウスカーソルが面の上に無い時は "Non!" と表示されています。

確認しやすそうな数値でBodyを作り、マウスを動かし
確認してみました。

動画の最後の方ですが、スプリクトを中止しマウスを動かしているのですが
(要は何のコマンドにも入っていない状態です)、マウスカーソル下の
面がハイライト状態になります。(スプリクトではなりません)
・・・と言うことはFusion360自体でどの面の上にマウスカーソルが
認識しているのではないかな? と感じているのですが・・・。
現状、ファイル内の全ての面との交差を取得し、複数交差した点と
カメラとの距離を測定し、最短となった点の座標値を取得し表示させています。
効率が非常に悪いんです。どうしたらこの面を取得できるのだろう???

全ての面を取得する

細々とFusion360APIも挑戦しているのですが、
イマイチFusion360APIも理解できていない上に、Pythonも理解できていないです。

考えている処理で、全ての面を取得したいのですが出来るだけ
素早く取得したいです。 ドキュメントから辿って行く事になるのですが
結構深い位置にあります。大体こんな感じですね。

Application
 L Product
   L Component
     L bRepBody
          L bRepFace

リスト? コレクション? 類は省いているので、正しくは無いのですが…。
実際にはComponentやBodyが複数あるので、良くあるListのflattenに
近い感覚なのですが、ん~思うようには書けませんでした。


先日のPowerMill同様、5000枚程面のあるプロジェクトで幾つかの書き方で
速度を比較してみました。

import adsk.core, adsk.fusion, traceback

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        
        des = adsk.fusion.Design.cast(app.activeProduct)
        import time
        testtimes = []
        
        #test1
        faces = []
        s = time.time()
        for comp in des.allComponents:
            for bBody in comp.bRepBodies:
                for face in bBody.faces:
                    faces.append(face)
        t = time.time()
        testtimes.append('test1 : All Face Count {} : time {:.2f}s'.format(len(faces),t - s))
        
        #test2
        faces = []
        s = time.time()
        faces = [face
                    for comp in des.allComponents
                    for bBody in comp.bRepBodies
                    for face in bBody.faces]
        t = time.time()
        testtimes.append('test2 : All Face Count {} : time {:.2f}s'.format(len(faces),t - s))
        
        #test3
        faces = []
        from itertools import chain
        s = time.time()
        bBodies = [comp.bRepBodies for comp in des.allComponents]
        faceslst = [bBody.faces for bBody in list(chain.from_iterable(bBodies))]
        faces = list(chain.from_iterable(faceslst))
        t = time.time()
        testtimes.append('test3 : All Face Count {} : time {:.2f}s'.format(len(faces),t - s))
        
        #test4
        faces = []
        s = time.time()
        faces = list(chain.from_iterable(
                [bBody.faces for bBody in 
                list(chain.from_iterable(
                [comp.bRepBodies for comp in des.allComponents]))]))
        t = time.time()
        testtimes.append('test4 : All Face Count {} : time {:.2f}s'.format(len(faces),t - s))
        ui.messageBox('\n'.join(testtimes))
        
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

test1 : for を利用した無難な?書き方。
test2 : Pythonならではの内包表記。
test3 : イテレ-タと内包表記の組み合わせ。
test4 : test3をムリムリ一行に書いたもの。

結果はこんな感じなのですが、
f:id:kandennti:20180603005908p:plain
イマイチ結果が安定しないです。
test2~4のどれが一番速いのか? が良くわからないのですが
test1だけではなさそうです。

デフォルト [コンポーネント/残り代] と異なる設定のツールパス名を取得3

こちらの続きです。
デフォルト [コンポーネント/残り代] と異なる設定のツールパス名を取得2 - C#ATIA

イロイロと探していた末、目的のコンポーネント数を取得する関数を発見しました。
f:id:kandennti:20180601155707p:plain
しかもやりたかった事がそのまま例題に・・・。

念の為お伝えしておくと

 object obj = model_components_thicknessset(entity('toolpath', '1'), 'surfaces', 0)

の様にしているとPMill落ちます。(恐らくマクロでは扱えない型で返って来るのでしょう)
sizeで利用するしか方法は無さそうです。

又、前回のマクロですがコンポーネント数をチェックしていたにも関わらず、正しく処理
されていませんでした。

//pm2018 macro
//Find_un_match_ToolPath.mac ver0.0.3
//デフォルト [コンポーネント/残り代] と異なる設定のツールパス名取得
//---------------------------------------------------------------------------------------------------
//ver0.0.1-完成
//ver0.0.2-コンポーネントチェック追加
//ver0.0.3-model_components_thicknessset関数使用
//               -GetCompMatchList関数 不具合修正
//---------------------------------------------------------------------------------------------------

function main() {
	//debug mode
	bool debug = 1 
	
	//check
	call CanExec()
	
	//start
	call Sw_Start()
	graphics lock

	INFOBOX NEW "***** Toolpath ThicknessSetValues info *****"
	INFOBOX CLEAR FORMAT
	
	//defalt ThicknessSetValues
	call PushInfobox('Getting Surface Defaults Info.' + crlf)
	string list def_tvInfo = {}
	int list def_cmp = {}
	call Get_defalt_ThicknessSetValuesInfo($def_tvInfo, $def_cmp)
	
	//filter
	call PushInfobox('Getting Toolpath Surface Parameter.' + crlf)
	string filter = ''
	call Initfilter($def_tvInfo, $filter)
	if $debug == 1 {
		call WriteFile(macro_path(false) + '\filter.txt' , $filter)
	}
	
	//pram match toolpath
	call PushInfobox('Getting Parameter match toolpath.' + crlf)
	entity list tps = folder('toolpath')
	$tps =  filter($tps , $filter)
	if $debug == 1 {
		call WriteFile(macro_path(false) + '\Parammatch.txt' , join(extract($tps, 'name'), crlf))
	}
	
	//components match toolpath
	call PushInfobox('Getting Components match toolpath.' + crlf)
	call GetCompMatchList($tps, $def_cmp, $tps)
	
	//un match
	call PushInfobox('Getting un match Toolpath.' + crlf + crlf)
	string list all_tps =  extract(folder('toolpath'), 'name') 
	string list match_tps =  extract($tps, 'name') 
	string list unmatch_tps =  subtract($all_tps, $match_tps)
	
	call PushInfobox('デフォルト [コンポーネント/残り代] と異なる設定のツールパスは以下のものです' + crlf)
	//call PushInfobox("It's toolpath that un match the 'Surface Defaults' values." + crlf)
	call PushInfobox(join($unmatch_tps, crlf))
	
	//finish
	if $debug == 1 {
		string tm = ''
		call Sw_GetTime($tm)
		call PushInfobox(crlf + $tm)
	}
	message info  'Done'
}

//start check
function CanExec() {
	string list msg = {}
	int dmy = 0
	//toolpath
	if size(folder('toolpath')) <1 {
		 $dmy = add_last($msg, 'There is no toolpath to check.')
	}
	//model
	if size(folder('model')) <1 {
		 $dmy = add_last($msg, 'The model has not been imported.')
	}
	if not is_empty($msg) {
		message warn join($msg, crlf)
		macro abort
	}
}

//Match components
function GetCompMatchList(entity list tps, int list def_cmp, output entity list out) {
	int list rng = {}
	call GetRangeLst(0, size($tps), $rng)
	$rng = reverse($rng)
	int dmy = 0
	string def = join($def_cmp,',')
	string list removeLst = {}
	foreach idx in $rng {
		int list cmp = {}
		call GetToolpathCompsCount($tps[$idx], $cmp) 
		if join($cmp,',') != $def {
			$dmy = add_last($removeLst,$tps[$idx].name)
		}
	}
	
	if is_empty($removeLst) {
		$out = $tps
		return
	}

	string list txts = {}
	foreach tp_name in $removeLst {
		$dmy = add_last($txts, 'name != "' + $tp_name +'"')
	}
	$out = filter($tps, join($txts, ' and '))
}

//Toolpath components
function GetToolpathCompsCount(entity tp, output int list comps) {
	int list rng = {}
	call GetRangeLst(0, size($tp.ThicknessSetValues), $rng)
	
	$comps = {}
	int dmy = 0
	foreach idx in $rng {
		$dmy = add_last($comps,  size(model_components_thicknessset($tp, 'surfaces', $idx)))
	}
}

//filter
function Initfilter(string list tvs, output string out) {
	string list prms = {'Mode', 'UseAxialThickness', 'Thickness', 'AxialThickness'}
	int list prm_rng = {}
	call GetRangeLst(0, size($prms), $prm_rng)
	
	int list tv_rng = {}
	call GetRangeLst(0, size($tvs), $tv_rng)
	
	$out = ''
	string list tmp = {}
	int dmy = 0
	string value = ''
	foreach tv_idx in $tv_rng {
		string list info = tokens($tvs[$tv_idx], ',')
		foreach prm_idx in $prm_rng {
			if $prm_idx == 0 {
				$value = '"' + $info[$prm_idx] + '"'
			} else {
				$value = $info[$prm_idx]
			}
			$dmy = add_last($tmp, 'ThicknessSetValues[' + string($tv_idx) + '].' + $prms[$prm_idx] + 

' == ' + $value)
		}
	}
	$out = join($tmp, ' and ')
	print = $out
}

//defalt ThicknessSetValues info
function Get_defalt_ThicknessSetValuesInfo(output string list prms, output int list comps) {
	string dmy_tp = ''
	call InitDmyTP($dmy_tp)
	$prms = {}
	$comps = {}
	call GetThickSetValInfo(entity('toolpath', $dmy_tp), $prms, $comps)
	DELETE TOOLPATH  $dmy_tp
}

//ThicknessSetValues info
function GetThickSetValInfo(entity tp, output string list prms, output int list comps) {
	object list tvs = $tp.ThicknessSetValues
	
	int list rng = {}
	call GetRangeLst(0, size($tvs), $rng)
	
	//string list mode = extract($tvs, 'Mode') //NG
	bool list useax = extract($tvs, 'UseAxialThickness')
	real list thic = extract($tvs, 'Thickness')
	real list axthic = extract($tvs, 'AxialThickness')
	
	$prms = {}
	int dmy = 0
	foreach idx in $rng {
		object tv =  $tvs[$idx]
		$dmy = add_last($prms,  $tv.Mode + ',' + $useax[$idx] + ',' + $thic[$idx] + ',' + $axthic[$idx])
	}
	$comps = {}
	call GetToolpathCompsCount($tp, $comps)
}	

//dammy toolpath
function InitDmyTP(output string out) {
	$out = new_entity_name('toolpath')
	IMPORT TEMPLATE ENTITY TOOLPATH TMPLTSELECTORGUI 'Finishing/Constant-Z-Finishing.002.ptf'
	FORM CANCEL STRATEGYSELECTOR\nFORM TOOLPATHIMPORT
	EDIT TOOLPATH $out REAPPLYFROMGUI
	YES
	FORM ACCEPT SFConstZFinishing
}

//** Support Function **

//range
function GetRangeLst(int start, int count, output int list lst) {
	int num = 0
	$lst = {}
	do {
		int dmy = add_last($lst, $start + $num)
		$num = $num + 1
	} while $num < $count
}

//infobox
function PushInfobox(string msg) {
	INFOBOX STYLE "NORMAL"
	INFOBOX APPEND  $msg
}

//clock on
function Sw_Start() {
	CLOCK RESET QUIT
	CLOCK ON QUIT	
}

//clock off
function Sw_GetTime(output string out) {
	string $TraceFilePath = macro_path(false) + "\clock.txt"
	string list txts = {}
	ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT
	TRACEFILE OPEN $TraceFilePath
	CLOCK OFF QUIT
	CLOCK PRINT QUIT
	TRACEFILE CLOSE
	ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
	
	FILE OPEN $TraceFilePath FOR READ AS Input
	FILE READ $txts FROM Input
	FILE CLOSE Input
	DELETE FILE $TraceFilePath

	$out = $txts[0]
}

//WriteFile
function WriteFile(string path ,string s) {
	FILE OPEN $path FOR WRITE AS file
	FILE WRITE $s TO file
	FILE CLOSE file
}

前回のコードでの結果
f:id:kandennti:20180601155740p:plain
今回のコードでの結果
f:id:kandennti:20180601155750p:plain
体感的には、十分使ってみようかな?と思えるぐらいになりました。
もうちょっと速くなって欲しいのと、デフォルト値の取得のために
ダミーのツールパスを作っているのを止めたい。方法あるかな?

PMillマクロ速度比べ

今更ながら、PMillマクロコードの書き方の違いでの実行速度の違いを
比較してみました。

適当なツールパスを20本作り、
・tool.name
・Rapid.IncrementalStartZ
・Thickness
の3項目を抜き出します。

〇テスト1
全てのツールパスをforeachでグルグル回し、取得したパラメータを
String型に追加していきます。ベタベタなヤツです。

	foreach tp in folder('toolpath') {
		$toolname = $toolname + ',' + $tp.tool.name
		$rapid = $rapid + ',' + $tp.Rapid.IncrementalStartZ
		$thickness = $thickness + ',' + $tp.Thickness
	}

rapid と thickness は本来Real型なのですが、何故かString型にStringとして
追加出来ました。暗黙変換でしょうか? 危険ですね・・・。
確か、多くの言語はString型に追加された際には、既存の文字分+追加分
の領域を新たに確保しコピーした後に、追加分が入るような処理をしていた
ような気がしたので、安易に考えても効率悪そうです。

〇テスト2
foreachでグルグル回し、String型に追加するのではなく一旦Listに
確保し、最後にJoinします。

	foreach tp in folder('toolpath') {
		$dmy = add_last($toolnameLst, $tp.tool.name)
		$dmy = add_last($rapidLst, string($tp.Rapid.IncrementalStartZ))
		$dmy = add_last($thicknessLst, string($tp.Thickness))
	}
	string test2a = join($toolnameLst, ',')
	string test2b = join($rapidLst, ',')
	string test2c = join($thicknessLst, ',')

String型に追加しない分Joinの処理があるので、どちらが効率が良いものか?

〇テスト3
グルグルやるのを止め、関数を利用し狙ったパラメータをガツンと抜き出しJoinします。

	$toolname2  = join(extract(folder('toolpath'), 'tool.name'), ',')
	$rapid2  = join(extract(folder('toolpath'), 'Rapid.IncrementalStartZ'), ',')
	$thickness2  = join(extract(folder('toolpath'), 'Thickness'), ',')

2つの作業を1行で行うので、コード自体も短いです。


全体的にはこんな感じです。

//pm2018 macro

function main() {
	string tm = ''	
	string list msg = {}
	int dmy = 0

	//test1
	string toolname = ''
	string rapid = ''
	string thickness = ''

	call Sw_Start()
	foreach tp in folder('toolpath') {
		$toolname = $toolname + ',' + $tp.tool.name
		$rapid = $rapid + ',' + $tp.Rapid.IncrementalStartZ
		$thickness = $thickness + ',' + $tp.Thickness
	}
	call Sw_GetTime($tm)	
	$dmy = add_last($msg, 'test1 :' + $tm)
	
	//test2
	string list toolnameLst = {}
	string list rapidLst = {}
	string list thicknessLst = {}
	
	call Sw_Start()
	foreach tp in folder('toolpath') {
		$dmy = add_last($toolnameLst, $tp.tool.name)
		$dmy = add_last($rapidLst, string($tp.Rapid.IncrementalStartZ))
		$dmy = add_last($thicknessLst, string($tp.Thickness))
	}
	string test2a = join($toolnameLst, ',')
	string test2b = join($rapidLst, ',')
	string test2c = join($thicknessLst, ',')
	call Sw_GetTime($tm)	
	$dmy = add_last($msg, 'test2 :' + $tm)

	//test3
	string toolname2 = ''
	string rapid2  = ''
	string thickness2 = ''
	
	call Sw_Start()
	$toolname2  = join(extract(folder('toolpath'), 'tool.name'), ',')
	$rapid2  = join(extract(folder('toolpath'), 'Rapid.IncrementalStartZ'), ',')
	$thickness2  = join(extract(folder('toolpath'), 'Thickness'), ',')
	call Sw_GetTime($tm)	
	$dmy = add_last($msg, 'test3 :'+ $tm)
	
	message info  join($msg, crlf)
}

//clock on
function Sw_Start() {
	CLOCK RESET QUIT
	CLOCK ON QUIT	
}

//clock off
function Sw_GetTime(output string out) {
	string $TraceFilePath = macro_path(false) + "\clock.txt"
	string list txts = {}
	ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT
	TRACEFILE OPEN $TraceFilePath
	CLOCK OFF QUIT
	CLOCK PRINT QUIT
	TRACEFILE CLOSE
	ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
	
	FILE OPEN $TraceFilePath FOR READ AS Input
	FILE READ $txts FROM Input
	FILE CLOSE Input
	DELETE FILE $TraceFilePath

	$out = $txts[0]
}

f:id:kandennti:20180530172816p:plain

圧倒的です。

過去にこちらを書きましたが、
PowerMillマクロを高速化? - C#ATIA
個人的には

GRAPHICS LOCK
GRAPHICS UKLOCK

は、ほぼ効果無いです。CATIAの様に面等を選択した際のハイライトを
止めてもらえる方が効果が有りそうな気もするのですが。
(大きな計算済みツールパスを、アクティブにしなければならない)

他にも、モデルを非表示、エクスプローラ-を非表示等試した事も有りますが
ほぼ効果がありません。

foreach piyo in hoge {
	if huga {
		dmy = add_last(lst, piyo.name)
	}
}

のような処理は

lst = extract(filter(hoge, huga), 'name')

のようなコードにした方が、圧倒的に速いです。(VBAにも欲しい)
これの逆で、同一パラメータに同一の値を設定する関数無いかなぁ。

DMUスペースアナリシスのセッション2

こちらのh2さんのコメントで、非常にありがたい情報を頂きました。
DMUスペースアナリシスのセッション - C#ATIA

結論から書くと、当方は素のHD2で DMU スペース・アナリシス のライセンス無しですが
セクションの作成が出来ました。
恐らく、

  Set s = Sect.Export

の状態は、Partファイルをロード(オープンじゃない)した状態になっているのでは
無いかと思います。

折角なので、サンプルを作ってみました。
確かに高速で断面を大量に作ります。

'vba sample_GuideCurve_DMU_Sections_ver0.0.1  using-'KCL0.0.12'  by Kantoku
'DMUスペースアナリシスのセクション
'ガイドカーブ指定し、分割数を入力することで
'セクションパートをTreeにぶら下げた状態で終了します

Option Explicit

Const DeflutSplitCount = 3 '分割数デフォルト

Sub CATMain()

    'ドキュメントのチェック
    If Not CanExecute("ProductDocument") Then Exit Sub
    
    'ガイドライン選択
    Dim SelElm As SelectedElement
    Set SelElm = SelectGuideCurve()
    If SelElm Is Nothing Then Exit Sub

    '分割数
    Dim SplitCount As Long
    SplitCount = InputSplitCount(DeflutSplitCount)
    If SplitCount < 1 Then Exit Sub
    Dim Ratios As Collection
    Set Ratios = InitRange(SplitCount)
    
    'マトリックス
    Dim AryMat As Variant
    AryMat = GetMat3dLst(SelElm, Ratios)
    
    'プロダクト
    Dim Prod As Product
    Set Prod = CATIA.ActiveDocument.Product
    
    'セクションコレクション
    Dim Sects As Object 'Sections
    Set Sects = Prod.GetTechnologicalObject("Sections")
    
    'セクションパート
    Dim SectDocs As Collection
    Set SectDocs = GetSectionDoc(Sects, AryMat)
    
    'インポート先
    Dim IptProd As Product
    Set IptProd = Prod.Products.AddNewComponent("Product", "")
    
    'インポート
    Call InportDoc(IptProd, SectDocs)
    
    'セクション削除
    Call Sects.Remove(Sects.Count)
    
    MsgBox "Done"
    
End Sub

'セクションインポート
Private Sub InportDoc(ByRef Prod As Product, ByRef SectDocs As Collection)
    Dim ProdsVar As Variant
    Set ProdsVar = Prod.Products
    
    Dim Doc As PartDocument
    For Each Doc In SectDocs
        ProdsVar.AddComponent Doc.Product
    Next
End Sub

'セクション-PartDoc
Private Function GetSectionDoc(ByVal Sects As Object, _
                               ByVal AryMat As Variant) As Collection
    Dim Sect As Object 'Section
    Set Sect = InitSection(Sects)
    
    Dim Docs As Collection
    Set Docs = New Collection
    
    Dim i As Long
    For i = 0 To UBound(AryMat)
        Call Sect.SetPosition(AryMat(i))
        If Not Sect.IsEmpty Then
            Call Docs.Add(Sect.Export())
        End If
    Next
    Set GetSectionDoc = Docs
End Function

'Section OJ
Private Function InitSection(ByVal Sects As Object) As Object 'Section
    'セクション追加
    Call Sects.Add
    Dim Sect As Object 'Section
    Set Sect = Sects.Item(Sects.Count)
    
    'モード変更
    '0-catSectionBehaviorManual
    '1-catSectionBehaviorAutomatic
    '2-catSectionBehaviorFreeze
    Sect.Behavior = 1
    
    '0-without clipping  1-clipping
    Sect.CutMode = 0
    
    Set InitSection = Sect
End Function

'断面用マトリックス
Private Function GetMat3dLst(ByVal CrvElm As SelectedElement, _
                             ByVal Ratios As Collection) As Variant
    Dim Pt As Part
    Set Pt = KCL.GetParent_Of_T(CrvElm.value, "Part")
    
    Dim Pnt As Variant 'HybridShapePointOnCurve
    Set Pnt = InitCrvOnPnt(CrvElm)
    
    Dim Pln As Variant 'HybridShapePlaneNormal
    Set Pln = InitCrvOnPlane(CrvElm, Pnt)
    
    Dim Drt As HybridShapeDirection
    Set Drt = InitDirection(Pln, Pt)
    
    Dim ratio As RealParam
    Set ratio = Pnt.ratio
    
    Dim AryMat() As Variant
    ReDim AryMat(Ratios.Count - 1)
    
    Dim Mat(11) As Variant
    Dim Ary(3) As Variant
    
    Dim idx As Long
    idx = 0
    Dim v As Variant
    For Each v In Ratios
        ratio.value = v
        Call Pt.UpdateObject(Pnt)
        Call Pt.UpdateObject(Pln)
        Call Pt.UpdateObject(Drt)
        
        '0-2
        Call Pln.GetFirstAxis(Mat)
        
        '3-5
        Call Pln.GetSecondAxis(Ary)
        Mat(3) = Ary(0)
        Mat(4) = Ary(1)
        Mat(5) = Ary(2)
        
        '6-8
        Mat(6) = Drt.GetXVal
        Mat(7) = Drt.GetYVal
        Mat(8) = Drt.GetZVal
        
        '9-11
        Call Pnt.GetCoordinates(Ary)
        Mat(9) = Ary(0)
        Mat(10) = Ary(1)
        Mat(11) = Ary(2)
        
        AryMat(idx) = Mat
        idx = idx + 1
    Next
    
    GetMat3dLst = AryMat
    
    '削除
    Dim fact As HybridShapeFactory
    Set fact = Pt.HybridShapeFactory
    
    Call fact.DeleteObjectForDatum(Drt)
    Call fact.DeleteObjectForDatum(Pln)
    Call fact.DeleteObjectForDatum(Pnt)
End Function

'範囲
Private Function InitRange(ByVal Count As Long) As Collection
    Dim Lst As Collection
    Set Lst = New Collection
    
    Dim stp As Double
    stp = 1# / (Count + 1)
    
    Dim i As Long
    For i = 0 To Count + 1
        Lst.Add i * stp
    Next
    Set InitRange = Lst
End Function

'方向
Private Function InitDirection(ByVal Pln As HybridShapePlaneNormal, _
                               ByVal Pt As Part) _
                               As HybridShapeDirection
    Dim fact As HybridShapeFactory
    Set fact = Pt.HybridShapeFactory

    Dim Ref As Reference
    Set Ref = Pt.CreateReferenceFromObject(Pln)
    
    Dim Drt As HybridShapeDirection
    Set Drt = fact.AddNewDirection(Ref)
    
    Call Pt.UpdateObject(Drt)
    Set InitDirection = Drt
End Function

'平面
Private Function InitCrvOnPlane(ByVal CrvElm As SelectedElement, _
                                ByVal Pnt As HybridShapePointOnCurve) _
                                As HybridShapePlaneNormal
    Dim Pt As Part
    Set Pt = KCL.GetParent_Of_T(CrvElm.value, "Part")
    
    Dim fact As HybridShapeFactory
    Set fact = Pt.HybridShapeFactory

    Dim Ref As Reference
    Set Ref = Pt.CreateReferenceFromObject(Pnt)

    Dim Pln As HybridShapePlaneNormal
    Set Pln = fact.AddNewPlaneNormal(CrvElm.Reference, Ref)

    Call Pt.UpdateObject(Pln)
    Set InitCrvOnPlane = Pln
End Function

'点
Private Function InitCrvOnPnt(ByVal CrvElm As SelectedElement) _
                              As HybridShapePointOnCurve
    Dim Pt As Part
    Set Pt = KCL.GetParent_Of_T(CrvElm.value, "Part")
    
    Dim fact As HybridShapeFactory
    Set fact = Pt.HybridShapeFactory

    Dim Pnt As HybridShapePointOnCurve
    Set Pnt = fact.AddNewPointOnCurveFromPercent(CrvElm.Reference, 0, False)
    
    Call Pt.UpdateObject(Pnt)
    Set InitCrvOnPnt = Pnt
End Function

'入力
Private Function InputSplitCount(ByVal def As Long) As Long
    Dim msg As String
    Dim tmp As Variant
    
    msg = "分割数を指定してください / 空白で終了" & vbCrLf & "両端は作成します"
    Do
        tmp = InputBox(msg, , def)
        Select Case True
            Case tmp = vbNullString
                InputSplitCount = -1
                Exit Function
            Case IsNumeric(tmp)
                If tmp >= 1 Then
                    InputSplitCount = CLng(tmp)
                    Exit Function
                End If
        End Select
        MsgBox "1以上の数字を入力して下さい", vbOKOnly + vbExclamation
    Loop
End Function

'ガイドライン選択
Private Function SelectGuideCurve() As SelectedElement
    Set SelectGuideCurve = Nothing
    Dim msg$
    msg = "ガイドラインを選択してください : ESCキー 終了"
    
    Dim SelElm As SelectedElement
    Dim Pt As Part
    
    Dim fact As HybridShapeFactory
    Dim Hs As HybridShape
    Do
        Set SelElm = KCL.SelectElement(msg, "HybridShape")
        If SelElm Is Nothing Then Exit Function
        
        Set Hs = SelElm.value
        Set Pt = KCL.GetParent_Of_T(Hs, "Part")
        Set fact = Pt.HybridShapeFactory
        Select Case fact.GetGeometricalFeatureType(SelElm.Reference)
            Case 2, 3, 4
                Set SelectGuideCurve = SelElm
                Exit Function
            Case Else
                MsgBox "直線,円弧,曲線 を選択してください"
        End Select
    Loop
End Function

ガイドラインを指定しラインの分割数を入力する事で分割数 + 始点 + 終点 分の
断面のCATPartをSubAssy状態でぶら下げて終了します。

実際にテストした感じです。

imihitoさん、h2さん の情報無しではここまで出来ませんでしたよ。
非常に感謝しております。
ライセンス無しでも出来る事が、少しはあるものですね。