C#ATIA

↑タイトル詐欺 主にCATIA V5 の VBA

スパインの理解

先日、思うところがあったため折角の機会だと思い・・・。

"スパイン" について説明がされているサイトを見かけないので、
僕なりの理解を記載しておきます。
(違うよ ってご意見等有りましたらよろしくお願いします)


実際に検索してみると
CATIA やUGNX などの三次元CADに関する質問です。ボディにフィ... - Yahoo!知恵袋
あまりに抽象的過ぎて質問者に伝わっていない気がしてます。

"Unofficial CATIA User Forum" では、ここなさんが
「スパインは画面上では線として表現されているけど、実際は平面の羅列です」
と言った表現をされていました。
個人的にはこの表現で、おぼろげだった理解がすっきりとしました。

簡単な例を。

XY平面・ZX平面にそれぞれスケッチを作成しスプラインを描きます。
この2本の線を合成コマンドで3D的な自由曲線を作成します。
f:id:kandennti:20171211142045p:plain

この曲線上に点を作成します。
f:id:kandennti:20171211142053p:plain

曲線と点を元にすると、接線を作成する事が出来ます。
f:id:kandennti:20171211142104p:plain
どんな曲線であっても、線上の点を指定すれば接戦を描くことは
可能です。(数学的には微分です)

この接線を方向ベクトルと捉え点を指定することで平面が
作成出来ます。
f:id:kandennti:20171211142113p:plain
・・・別に接線を作成しなくても、同一の平面は作成可能なのですが
接線が平面の向きになることをお伝えしたかった為です。

つまり、どんな曲線であっても曲線上の点は接線ベクトルを持ち
平面が作成可能だということになります。

GSDにはこんなコマンドがあるので、イメージとしてはこんな感じです。
f:id:kandennti:20171211142124p:plain
「線に見えるけど、平面の羅列」とはこんな事を意味していると
解釈しました。


では、実際のモデリングでは何を意味するのでしょうか?

XY平面に四角を描いた上、2つの面に45度の勾配を付けます。
f:id:kandennti:20171211142137p:plain

この2つの45度面が交差するエッジ部分にエッジフィレットを付けてみます。
(仮にサイズ10mmです)
f:id:kandennti:20171211142152p:plain
まぁフィレット面が出来るだけなのですが、ここでフィレットを作成した際に
指定したRのサイズが何処に存在するのでしょうか?

一見、端部の形状が指定したRサイズになっているようにも感じますが、
実際に測定すると違う事がわかります。
f:id:kandennti:20171211142200p:plain
では何処に? ここでスパインが登場します。

履歴を戻り、フィレット作成した時に指定したエッジを使用しエッジに直交する
平面を作成します。
f:id:kandennti:20171211142227p:plain
最初に説明したように、どんな曲線(今回は直線)であっても、
平面の作成が可能ですね。

履歴を進み、先程の平面で分割。 切り口となったフィレット面の
端部を測定すると、指定したRサイズが見つかりました。
f:id:kandennti:20171211142236p:plain
つまりエッジフィレットは
”指定したエッジをスパインとするR曲面を作成する”
コマンドだったわけです。全ての3DCADを調べることは現実的では
無いのですが、恐らく全ての3DCADでは同じ事だと思っています。
実際はスパインを指定するわけでは無いのですが、自動的に
設定されているのだろうと思います。

逆にフィレットの端部を指定したRサイズとしたい場合は、
除変フィレットとし円形フィレットのスパインをZ軸とすれば
(エッジフィレットではスパインの指定が出来ません)
f:id:kandennti:20171211142248p:plain
意図していた形状になると思います。
f:id:kandennti:20171211142259p:plain
このようなフィレットが欲しい時は、あまり無いとは思いますが・・・。

GSDの場合、スイープ・ロフト・ブレンド辺りのコマンドでスパイン指定可能な
ものが多いのですが、細かな違いは有れど
指定した断面が、どの平面上に存在しているか?
を示すのがスパインなんだろうと思っています。


少し前にこちらを投稿したのですが
解決済み: R形状のサーフェイスの作り方について - Autodesk Community
本当はスパインについても書こうと思っていたのですが
Fusion360にはスパインのコマンドが無く、無い機能について
グダグダ書いても仕方ないと思い、こちらに記載してみました。

背景色を変更・復元する3

こちらの続きで、保留していた宿題です。
背景色を変更・復元する2 - C#ATIA

ご要望としては、PartでもProductでも同様の処理を行いたい
と言うことなので、修正してみました。

PartDocumentの取得方法を変更する必要があるため、
こちらの部分を修正します。

Private Sub UserForm_Initialize()
    'Form設定
    Me.CommandButton1.Caption = "背景色-白"
    Me.CommandButton2.Caption = "背景色-復元"
    
    '一時的な背景色
    mBackColor = Array(255, 255, 255)
    
    'CATIA情報
    Set mDoc = Get_PartDoc(CATIA.ActiveDocument)
    If mDoc Is Nothing Then
        MsgBox "Partファイルが見つかりません"
        Me.CommandButton1.Enabled = False
        Me.CommandButton2.Enabled = False
        Exit Sub
    End If
    Set mPt = mDoc.Part
    Set mSel = mDoc.Selection
    Set mVis = mSel.VisProperties
    Set mVisSetAtt = CATIA.SettingControllers.Item( _
        "CATVizVisualizationSettingCtrl")
End Sub

そして実際にPartDocumentを取得する為の関数を
追加します。(再帰で一番上部にあるPartDocumentを探しています)

Private Function Get_PartDoc(Doc As Document) As PartDocument
    If Not Get_PartDoc Is Nothing Then Exit Function
    
    Dim Pros As Products
    Dim i As Long
    
    Select Case TypeName(Doc)
        Case "PartDocument"
            Set Get_PartDoc = Doc
            Exit Function
    
        Case "ProductDocument"
            Set Pros = Doc.Product.Products
            If Pros.Count < 1 Then Exit Function
            
            For i = 1 To Pros.Count
                Set Get_PartDoc = _
                    Get_PartDoc(Pros.Item(i).ReferenceProduct.Parent)
                If Not Get_PartDoc Is Nothing Then Exit Function
            Next
        
        Case Else
            Exit Function
    End Select
End Function

Part・Product両方で動くマクロを作成する際、結論としては
どうやってPartDocumentを取得するか? だけの違いで他の部分は
共通です行えます。

Productの場合、例えデザインモードでPartファイルを操作している状態で
あっても

   set Doc = CATIA.ActiveDocument

を実行した場合、一番上のProductのDocumentが取得されます。
(子ウィンドウの一番上と言うイメージです)

Fusion360のスケルトン設計

ご質問頂いている件の修正をしなければならないと思っているのですが、
まとまった時間の確保が出来ず、後回しになってすいません。
今週中は無理っぽい雰囲気です。

自宅では少しずつFusion360をいじっているのですが、パワフルなので
"スケルトン設計が出来るのかも" と思い少し調べてみました。

・・・まともそうなのが、こちらの動画だけでした。
Using Skeletal Modeling Technique in Fusion 360 | Fusion 360 | Autodesk Knowledge Network
ケルトン設計(モデリング)の定義が良くわかっていないのですが、
パラメータで管理することなのでしょうか???
個人的にはスケルトンとなるデータを作り、管理することなのかな?
と思っていたのですが。

こちらはInventorのスケルトン設計の資料。
http://images.autodesk.com/apac_japan_main/files/skeleton_pipeasm.pdf
こっちも、まずパラメータ・・・そうなのかな・・・


定義は兎も角、実際に試してみると、一応ファイル間リンクは保っているのですが
取り込む際は重いです。 但し、参照先が最新かどうかは管理してくれていますね。
(日本語フォーラムを見ると、リンクを切りたい人が多いようですが)

これ、個人的には個別のボディやスケッチのみを取り込みたい所なのですが
丸ごとしか出来ないような気がします。

Fusion360のファイル(.f3d)は、CATIAのPartファイルのようで
Productファイルのような側面もあり(コンポーネント機能)、ファイル間リンクより
ルール付けをしながら単一ファイルでの管理の方がスッキリしそうな気がします。
(ボディのリンクの結果によるコピペは可能そう)

樹木曲線も作りたかったのに

こちらの続きです。
コッホ曲線を描く - C#ATIA

中央付近にある、樹木曲線もCATVBA化しようと思ったのですが
エラーになりました。

よく見るとオリジナルのコードですが

・・・
   public void paint(Graphics g){

      //3対の点を指定します
      Point P=new Point(100,400);
      Point Q=new Point(100,100);
・・・

で、点の座標値を設定し

・・・
      //それぞれの対をなす2点間に樹木曲線を描きます
      g.setColor(Color.blue);
      drawTree(g,P,Q,3);
・・・

先程の2点をdrawTree関数に投げてます。

・・・
   //樹木曲線を描くメソッド
   public void drawTree(Graphics g,Point a,Point b,int n){

・・・
 
      xx=b.x-a.x;
      yy=-(b.y-a.y);

      angle1=Math.atan((double)yy/xx)+Math.PI/6;
・・・

drawTree関数側では "xx" を求めるのに、お互いのx座標値の
差分を取得してます。
その後、 "angle1" を求める際、先程の "xx" で割っているのですが、
最初に設定した座標値のお互いの差分は "0" なのです。(100-100)

つまり、余裕でゼロ除算でエラーと言うわけです。 なんでだろう?
JAVAは 0の逆数を発見したのだろうか?
(業務が異常に忙しい・・・)

コッホ曲線を描く

こちらの12年も前の記事なのですが、楽しそうなので
CATIAで行ってみました。
再帰プログラムによるフラクタル図形の描画:CodeZine(コードジン)

'vba sample_Koch_Curve_ver0.0.1  using-'KCL0.0.12'  by Kantoku
'xy平面上にコッホ曲線を作成します
'https://codezine.jp/article/detail/73

Option Explicit

Private Const LEVEL = 3         '再帰レベル

Dim mPnts As Object             'Get_KochCurvePos用座標郡
Dim mDoc As PartDocument
Dim mPt As Part
Dim mFact As HybridShapeFactory

'定数代わり
Dim m1_3 As Double              '1/3
Dim m1_Sq3 As Double            '1/sqr(3)
Dim mPI_6 As Double             'PAI/6 = 30deg

Sub CATMain()
    'ドキュメントのチェック
    If Not CanExecute("PartDocument") Then Exit Sub
    
    '初期設定
    m1_3 = 1 / 3
    m1_Sq3 = 1 / Sqr(3)
    mPI_6 = Atn(1) * 4 / 6
    
    Set mDoc = CATIA.ActiveDocument
    Set mPt = mDoc.Part
    Set mFact = mPt.HybridShapeFactory
    
    '頂点座標
    Dim p1, p2, p3
    p1 = Array(100#, 160#)
    p2 = Array(400#, 160#)
    p3 = Array(250#, 420#)
    
    '座標値取得
    Set mPnts = KCL.InitLst()
    mPnts.Add p1
    
    Call Get_KochCurvePos(p1, p2, LEVEL)
    Call Get_KochCurvePos(p2, p3, LEVEL)
    Call Get_KochCurvePos(p3, p1, LEVEL)
    
    '座標値→点リファレンス
    Dim Refs As Object
    Set Refs = Get_PointRefs(mPnts)
    
    '折れ線化
    Dim Poly  As HybridShapePolyline
    Set Poly = Init_Poly(Refs)
    
    '形状セットへ挿入
    Dim Hbdy As HybridBody
    Set Hbdy = mPt.hybridBodies.Add()
    Hbdy.Name = "Koch_Curve"
    
    Hbdy.AppendHybridShape Poly
    
    'Refsの最後の点のみ不要
    mFact.DeleteObjectForDatum Refs(Refs.Count - 1)
    
    '終わり
    MsgBox "Done"
End Sub

'リファレンスリストから折れ線生成
Private Function Init_Poly(ByVal Refs As Object) As HybridShapePolyline
    Dim Poly As HybridShapePolyline
    Set Poly = mFact.AddNewPolyline()
    
    Dim i As Long
    For i = 0 To Refs.Count - 2
        Poly.InsertElement Refs(i), i
    Next
    Poly.Closure = True
    mPt.UpdateObject Poly
    
    Set Init_Poly = Poly
End Function

'xy座標値郡から点のリファレンスリスト生成
Private Function Get_PointRefs(ByVal Lst As Object) As Object
    Dim Refs As Object
    Set Refs = KCL.InitLst
    
    Dim p As Variant
    For Each p In mPnts
        Refs.Add Init_PointRef(p)
    Next
    
    Set Get_PointRefs = Refs
End Function

'xy座標値から点のリファレンス生成
Private Function Init_PointRef(ByVal Ary As Variant) As Reference
    Dim p As HybridShapePointCoord
    Set p = mFact.AddNewPointCoord(Ary(0), Ary(1), 0#)
    mPt.UpdateObject p
    
    Set Init_PointRef = mPt.CreateReferenceFromObject(p)
End Function

'コッホ曲線座標
Private Sub Get_KochCurvePos(ByVal p1 As Variant, ByVal p2 As Variant, lv As Long)
    Dim p3 As Variant, p4 As Variant, p5 As Variant
    p3 = Array((2 * p1(0) + p2(0)) * m1_3, (2 * p1(1) + p2(1)) * m1_3)
    p4 = Array((p1(0) + 2 * p2(0)) * m1_3, (p1(1) + 2 * p2(1)) * m1_3)
    
    Dim xx As Double, yy As Double
    xx = p2(0) - p1(0)
    yy = -(p2(1) - p1(1))
    
    Dim dist As Double
    dist = Sqr(xx * xx + yy * yy) * m1_Sq3
    
    Dim ang As Double
    If xx >= 0 Then
        ang = Atn(yy / xx) + mPI_6
        p5 = Array(p1(0) + (dist * Cos(ang)), p1(1) - (dist * Sin(ang)))
    Else
        ang = Atn(yy / xx) - mPI_6
        p5 = Array(p2(0) + (dist * Cos(ang)), p2(1) - (dist * Sin(ang)))
    End If
    
    If lv < 1 Then
        mPnts.Add p3
        mPnts.Add p5
        mPnts.Add p4
        mPnts.Add p2
    Else
        Call Get_KochCurvePos(p1, p3, lv - 1)
        Call Get_KochCurvePos(p3, p5, lv - 1)
        Call Get_KochCurvePos(p5, p4, lv - 1)
        Call Get_KochCurvePos(p4, p2, lv - 1)
    End If
End Sub

再帰でやるのもどうかな? とも思ったのですが、再帰の為の記事だったので
再帰のままです。

元の記事では、座標値を計算しながら線を描いてますが、
まとめて座標値を取得し、一本一本描かずに折れ線化しています。
(他にも計算コストの高そうな部分は、予めメンバ変数化し計算量を減らしてます)

円を指定して内接するようにしようかとも思いましたが、
利用価値が低そうなので、単にXY平面に描くだけにしました。

見ていたら雪の結晶を思い出し、よけいに寒くなりました・・・。

背景色を変更・復元する2

こちらの続きです。
背景色を変更・復元する - C#ATIA

"パラメータを作成するのに処理時間がかかる" との事でしたので、
代案として、XY平面に元の背景色をバックアップする事にしました。

又、Formのボタンで操作可能なもののサンプルにして見ました。

まず、新作したFormにコマンドボタンを2つ配置します。
それぞれの名前はそのままなのですが、念の為記載しておきます。
・UserForm1
・CommandButton1
・CommandButton2
f:id:kandennti:20171120162151p:plain

このフォームのコード部分に以下のコードを貼り付けます。

'vba sample_ChangeBackColor ver0.0.2 by Kantoku
'CATIAの背景色を変更・復元
Option Explicit

'メンバ変数
Dim mDoc As PartDocument
Dim mPt As Part
Dim mSel As Selection
Dim mVis As VisPropertySet
Dim mVisSetAtt As VisualizationSettingAtt
Dim mBackColor As Variant

Private Sub UserForm_Initialize()
    'Form設定
    Me.CommandButton1.Caption = "背景色-白"
    Me.CommandButton2.Caption = "背景色-復元"
    
    '一時的な背景色
    mBackColor = Array(255, 255, 255)
    
    'CATIA情報
    Set mDoc = CATIA.ActiveDocument
    Set mPt = mDoc.Part
    Set mSel = mDoc.Selection
    Set mVis = mSel.VisProperties
    Set mVisSetAtt = CATIA.SettingControllers.Item( _
        "CATVizVisualizationSettingCtrl")
End Sub

'コマンドボタン1
Private Sub CommandButton1_Click()
    Call ChangeBackColor
End Sub

'コマンドボタン2
Private Sub CommandButton2_Click()
    Call RestorationBackColor
End Sub

' --- サポート関数 ---

'BACKCOLORに変更
Private Sub ChangeBackColor()
    Dim Color As Variant
    
    '背景色取得
    Color = Get_BackRGB()
    
    'XY平面に割り当て
    Call Set_PlaneRGB(mPt.OriginElements.PlaneXY, Color)
    
    '背景色変更
    Call Set_BackRGB(mBackColor)
End Sub

'復元
Sub RestorationBackColor()
    Dim Color As Variant
    
    'XY平面色取得
    Color = Get_PlaneRGB(mPt.OriginElements.PlaneXY)
    
    '背景色復元
    Call Set_BackRGB(Color)
    
    'YZ平面色取得
    Color = Get_PlaneRGB(mPt.OriginElements.PlaneYZ)
    
    'XY平面色復元
    Call Set_PlaneRGB(mPt.OriginElements.PlaneXY, Color)
End Sub

Private Function Set_PlaneRGB(ByVal Plane As Plane, _
                              ByVal Color As Variant)
    mSel.Clear
    mSel.Add Plane
    
    mVis.SetRealColor Color(0), Color(1), Color(2), 1
    mSel.Clear
End Function

Private Function Get_PlaneRGB(ByVal Plane As Plane)
    mSel.Clear
    mSel.Add Plane
    
    Dim Color(2) As Long
    mVis.GetRealColor Color(0), Color(1), Color(2)
    mSel.Clear
    Get_PlaneRGB = Color
End Function

Private Sub Set_BackRGB(ByVal Color As Variant)
    Call mVisSetAtt.SetBackgroundRGB( _
        Color(0), Color(1), Color(2))
    mVisSetAtt.SaveRepository
End Sub

Private Function Get_BackRGB() As Variant
    Dim Color(2) As Long
    Call mVisSetAtt.GetBackgroundRGB( _
        Color(0), Color(1), Color(2))
    Get_BackRGB = Color
End Function

"例外処理は要らない" との事なので、ほぼしておりません。
念の為、
・PartDocument以外はエラーになります。
・背景色復元後、XY平面色も復元させていますが、
 その際YZ平面色をXY平面に反映させています。(通常同じだろうと・・・)
・2度続けて、"背景色-白" ボタンを押すと、復元不可能になります。

StartCommandとRefreshDisplay

別のものを作成しているうちに気が付いたので覚書なのですが、
ひょっとしたら既出なのかも知れません。


CATIAのマクロでは、全てのコマンド類を実行する為の関数類が提供されて
いるわけではないのですが、

CATIA.StartCommand xxxx(コマンド文字列)

を利用すると、手動での実行時のダイアログが出現する為、不可能と
思われたことが可能になる場合が有ります。
こちらで作成したシルエットコマンドの無意味なサンプルも、そんな方法です。
シルエット(事前選択+CATIA.StartCommand) - C#ATIA

但し、困るのは "StartCommand" は、ダイアログを呼び出してくれる
だけなので、"事前選択" と "SendKeys" を利用する事になります。
・・・が、"SendKeys" がナカナカの曲者で、思ったようなタイミングでは
文字を送ってくれないです。


そんな例として、一つ。
手動の場合は事前に面を選択し形状セットを作ると、子の要素として
取り入れた状態で、形状セットが新たに作成されます。
言葉では伝わりにくいので、こんな感じの操作です。

 
形状セットを作る為の関数は用意されているのですが、子の要素を
取り入れながら形状セット新たに作成する関数は存在していないです。
恐らく。
こう言った場合、"StartCommand" を利用する以外には方法が無さ
そうなので、こんな感じのコードを作成しました。

'vba これNGです using-'KCL0.0.12'
'選択した要素を子とする形状セットを作成

'コマンド文字列
Private Const CMD = "形状セット..."

Sub CATMain()
    Dim Shp As HybridShape
    Set Shp = KCL.SelectItem("GSD要素を選択", "HybridShape")
    If Shp Is Nothing Then Exit Sub
    
    Dim Sel As Selection
    Set Sel = CATIA.ActiveDocument.Selection
    
    Sel.Clear
    Sel.Add Shp
    
    CATIA.StartCommand CMD
    SendKeys "{Enter}", True
End Sub

子の要素を選択状態のまま、形状セットを作成するコマンドを呼び出し、
最後にEnterキーを投げています。
悪くもない感じがするのですが、実際に実行してみるとこのような
ダイアログが表示された状態で終わってしまします。
f:id:kandennti:20171120142509p:plain

    SendKeys "{Enter}", True

の部分が上手く行ってないです。
こんな感じのものや
http://www.geocities.co.jp/SiliconValley-PaloAlto/9180/exsendkeys.html
ウェイトさせたりしても変化無しでした。

試していて感じたのは、ダイアログが表示されるのが遅いのではなく、
マクロの処理を待った上で、ダイアログが表示されるような気さえします。

そこでこんな感じ1行追加しただけで、上手く行くようになりました。

'vba sample_Init_HybridBody_InsItem_ver0.0.1  using-'KCL0.0.12'  by Kantoku
'選択した要素を子とする形状セットを作成

'コマンド文字列
Private Const CMD = "形状セット..."

Sub CATMain()
    Dim Shp As HybridShape
    Set Shp = KCL.SelectItem("GSD要素を選択", "HybridShape")
    If Shp Is Nothing Then Exit Sub
    
    Dim Sel As Selection
    Set Sel = CATIA.ActiveDocument.Selection
    
    Sel.Clear
    Sel.Add Shp
    
    CATIA.StartCommand CMD
    CATIA.RefreshDisplay = True '追加
    SendKeys "{Enter}", True
End Sub

RefreshDisplayを間に挟んだところ、Enterキーを認識してくれました。


単に画面の更新を入れるだけで、良かったみたいです。
少しだけ "StartCommand" の利用する場面が増えそうな気がします。
本当は、 "形状セットの変更" をやりたいんですけどね。