曲線を交差しているエレメント毎に分割するマクロ|CATIAマクロの作成方法
今回の記事は「お問い合わせ」よりいただいた内容です。
送って頂いた内容は以下のようなマクロです。
ワークベンチ: GSDワークベンチ
マクロ案:
長い曲線に対して、交差している曲線が複数有、長い曲線を交差している部分で分割し細かくしたいのですが、分割すると次回の分割 時に長い曲線が分割されて複数有る状態なのでどれが交差している 長い曲線か分かりません。
出来れば、形状2つ作成し1つは分割する用の曲線を入れもう一つに交差する曲線を入れて総当たりで分割するマクロは出来ないでし ょうか?
文字と合わせて画像もご連絡していただきましたが、イメージとしては下画像のような長い曲線(白)と短い曲線(ピンク)があり、交差しているところで長い曲線(白)を分割したいという感じです。つまり下画像でいえば、長い曲線(白)を交差しているところで3つに分割する処理をしたいということです。
この処理は「分割(Split)する向き」「どこで分割するか」「分割する要素となる短い曲線はどれを使うか」などをすべてプログラム上で認識させないといけないため、サンプルコードの内容は少し難解なものとなっています。
人間の目で見れば簡単な処理ですが、プログラムで書こうとすると実はハードルの高い処理です。
(上画像の長い曲線を3分割にするという処理だけでもなかなかの難易度だと思います。)
マクロの機能
今回作成したのは選択した2つの形状セット内の曲線と曲線が交差しているところで分割した曲線を出力するマクロです。
言葉だけだとわかりづらいですが冒頭でも紹介した通り、長い曲線を短い曲線で細かく分割して出力する処理を行います。(上画像の場合、[曲線.3]は5分割され、[曲線.4]は3分割されています)
具体的な機能は以下のとおりです。
※「長い曲線が入った形状セット」→「短い曲線が入った形状セット」の順で選択
・分割した曲線はデータム化(Isolate)して出力
・Part直下に「CurveCutMacro」という形状セットが作成されその中に出力していく
・分割された曲線は長い曲線ごとにセット分けされて「CurveCut-X」という名前で出力
サンプルコード
マクロのコードは以下のとおりです。
コードがかなり長いのですべての詳しい解説はしませんが、コード解説では「どのような考えの処理をしているか」について説明していきます。
Option Explicit
Sub CATMain()
'アクティブドキュメント等の定義
If TypeName(CATIA.ActiveDocument) <> "PartDocument" Then
MsgBox "このマクロはPartDocument専用です。" & vbLf & _
"CATPartに切り替えて実行してください。"
Exit Sub
End If
Dim doc As PartDocument: Set doc = CATIA.ActiveDocument
Dim pt As Part: Set pt = doc.Part
Dim hsf As HybridShapeFactory: Set hsf = pt.HybridShapeFactory
Dim sel: Set sel = doc.Selection
sel.Clear
'形状セット(長い曲線)を取得
Dim res As String
Dim Filter: Filter = Array("HybridBody")
res = sel.SelectElement2(Filter, "長い曲線の入った形状セットを選択してください。", False)
If res <> "Normal" Then
MsgBox "キャンセルしました。"
Exit Sub
End If
Dim input_hb1 As HybridBody
Set input_hb1 = sel.Item(1).Value
sel.Clear
'形状セット(短い曲線)を取得
res = sel.SelectElement2(Filter, "短い曲線の入った形状セットを選択してください。", False)
If res <> "Normal" Then
MsgBox "キャンセルしました。"
Exit Sub
End If
Dim input_hb2 As HybridBody
Set input_hb2 = sel.Item(1).Value
sel.Clear
'作業用形状セットの作成
Dim output_hb As HybridBody
Set output_hb = pt.HybridBodies.Add
output_hb.Name = "CurveCutMacro"
Dim p
Dim ps As Collection
Dim hs1 As HybridShape
Dim hs2 As HybridShape
Dim ref_hs1 As Reference
Dim ref_hs2 As Reference
Dim crv
Dim chk
Dim spl1 As HybridShapeSplit
Dim spl2 As HybridShapeSplit
Dim ref_p As Reference
Dim ref_spl As Reference
Dim ref_crv As Reference
Dim output As Collection
'長い曲線ループ
For Each hs1 In input_hb1.HybridShapes
Set ref_hs1 = pt.CreateReferenceFromObject(hs1)
Dim crv_hb As HybridBody
Set crv_hb = output_hb.HybridBodies.Add
crv_hb.Name = hs1.Name
Dim p_hb As HybridBody
Set p_hb = crv_hb.HybridBodies.Add
p_hb.Name = "point"
'短い曲線ループ 総当たりで全ての交差(Intersection)作成
For Each hs2 In input_hb2.HybridShapes
Set ref_hs2 = pt.CreateReferenceFromObject(hs2)
Dim sect As HybridShapeIntersection
Set sect = hsf.AddNewIntersection(ref_hs1, ref_hs2)
p_hb.AppendHybridShape sect
On Error Resume Next
pt.UpdateObject sect
If Err.Number = -2147467259 Then
sel.Add sect
sel.Delete
End If
On Error GoTo 0
sect.Name = hs1.Name & " & " & hs2.Name
Next hs2
'全ての交差と始点,終点をコレクション「ps」に格納
Set ps = New Collection
For Each p In p_hb.HybridShapes
ps.Add p
Next p
'始点の作成
Dim sp As Point
Set sp = hsf.AddNewPointOnCurveFromPercent(ref_hs1, 0, False)
p_hb.AppendHybridShape sp
pt.UpdateObject sp
sp.Name = "Start Point"
'終点の作成
Dim ep As Point
Set ep = hsf.AddNewPointOnCurveFromPercent(ref_hs1, 1, False)
p_hb.AppendHybridShape ep
pt.UpdateObject ep
ep.Name = "End Point"
Dim spa As SPAWorkbench
Dim m As Measurable
Dim md As Double
Dim ref_sp As Reference
Dim ref_ep As Reference
Dim p_chk As Boolean
Dim p_cnt As Long
Dim omit_p
Dim omit_ps As Collection
Set spa = doc.GetWorkbench("SPAWorkbench")
Set omit_ps = New Collection
Set ref_sp = pt.CreateReferenceFromObject(sp)
Set ref_ep = pt.CreateReferenceFromObject(ep)
'始点と全ての交差の距離を測定し,距離が0の場合は「omit_ps」に格納
Set m = spa.GetMeasurable(ref_sp)
For Each p In ps
Set ref_p = pt.CreateReferenceFromObject(p)
md = m.GetMinimumDistance(ref_p)
If md = 0 Then
p_chk = True
omit_ps.Add p
End If
Next p
'終点と全ての交差の距離を測定し,距離が0の場合は「omit_ps」に格納
Set m = spa.GetMeasurable(ref_ep)
For Each p In ps
Set ref_p = pt.CreateReferenceFromObject(p)
md = m.GetMinimumDistance(ref_p)
If md = 0 Then
p_chk = True
omit_ps.Add p
End If
Next p
If p_chk = True And ps.Count = 1 Then
sel.Add output_hb
sel.Delete
MsgBox "形状を作成できません。"
Exit Sub
End If
'「omit_ps」内に入っている交差を「ps」内から除外
lab:
p_cnt = 1
For Each p In ps
For Each omit_p In omit_ps
If p.Name = omit_p.Name Then
ps.Remove p_cnt
GoTo lab
End If
Next omit_p
p_cnt = p_cnt + 1
Next p
'始点と終点を「ps」に格納
'→ 始点,終点,交差(始点,終点と同じ位置にある交差は除く)の入ったコレクション
ps.Add sp
ps.Add ep
'分割(Split)格納用形状セットを作成
Dim spl_hb As HybridBody
Set spl_hb = crv_hb.HybridBodies.Add
spl_hb.Name = "split"
Set output = New Collection
Set ref_crv = ref_hs1
'分割(Split)の作成
If ps.Count > 2 Then
On Error Resume Next
Do
For Each p In ps
Set ref_p = pt.CreateReferenceFromObject(p)
Set spl1 = hsf.AddNewHybridSplit(ref_crv, ref_p, 1)
spl_hb.AppendHybridShape spl1
pt.UpdateObject spl1
'交差している点の数をカウント
chk = 0
chk = GetSectPointsCount(spl1, ps)
'交差している点の数が2つの場合の分割(Split)以外は削除
If chk = 2 Then
output.Add spl1
Set spl2 = hsf.AddNewHybridSplit(ref_crv, ref_p, -1)
spl_hb.AppendHybridShape spl2
pt.UpdateObject spl2
If spl_hb.HybridShapes.Count = (ps.Count - 1) * 2 - 2 Then
output.Add spl2
End If
Exit For
Else
sel.Add spl1
sel.Delete
End If
Next p
Set crv = spl_hb.HybridShapes.Item(spl_hb.HybridShapes.Count)
Set ref_crv = pt.CreateReferenceFromObject(crv)
Loop Until output.Count = ps.Count - 1
On Error GoTo 0
'アウトプットのデータム(Datum)作成
Dim cnt As Long
cnt = 1
For Each crv In output
Set ref_crv = pt.CreateReferenceFromObject(crv)
Dim dtm_crv
Set dtm_crv = hsf.AddNewCurveDatum(ref_crv)
crv_hb.AppendHybridShape dtm_crv
pt.UpdateObject dtm_crv
dtm_crv.Name = "CurveCut-" & cnt
cnt = cnt + 1
Next crv
sel.Add p_hb
sel.Add spl_hb
sel.Delete
Else
sel.Add crv_hb
sel.Delete
End If
Next hs1
End Sub
'****************************************************************************
' 関数内容:コレクション「ps」内のpointとcrvの交差している数をカウントして返す
'****************************************************************************
Function GetSectPointsCount(ByVal crv As HybridShape, ByVal ps As Collection) As Long
Dim doc As PartDocument: Set doc = CATIA.ActiveDocument
Dim pt As Part: Set pt = doc.Part
Dim spa As Workbench
Dim m As Measurable
Dim md As Double
Dim p
Dim ref_p As Reference
Dim ref_crv As Reference
Dim cnt As Long
Set spa = doc.GetWorkbench("SPAWorkbench")
Set ref_crv = pt.CreateReferenceFromObject(crv)
Set m = spa.GetMeasurable(ref_crv)
For Each p In ps
Set ref_p = pt.CreateReferenceFromObject(p)
md = m.GetMinimumDistance(ref_p)
If md = 0 Then
cnt = cnt + 1
End If
Next p
GetSectPointsCount = cnt
End Function
コード解説
本マクロでは分割(Split)がメイン処理です。
という訳でまずは分割を作成する方法を紹介しておきます。
VBAで分割を作成するにはHybridShapeFactoryオブジェクトのAddNewHybridSplitメソッドを使い、以下のように書きます。下記コードによりref1をref2で分割することができます。また、分割方向を反転するには最後の引数を「1」から「-1」にします。
Dim spl As HybridShapeSplit
Set spl = HybridShapeFactoryオブジェクト.AddNewHybridSplit(ref1, ref2, 1)
あとは上記コードを使って長い曲線を短い曲線で分割していけばいいだけのお話ですが、そう単純にいかないのが今回の難しいところです。
たとえば下のような長い曲線(水色)と短い曲線(ピンク)があるとします。
短い曲線が下画像の①~⑥の順番、もしくは⑥~①の順番で並んでいれば、端のものから順番に分割していけば問題なく作成できます。
しかし、選択する形状セット内にある短い曲線が上画像の通り、端から順番になっていないことを考えるとその処理ではうまく処理ができません。
そこで本マクロでは「分割した後の長い曲線と交差する"交差点の数"と"分割後の曲線の端点"の総和をカウントする」という考えで処理を行っています。
たとえば下図のように④の曲線で分割した場合を考えると、「分割後の曲線と交差する点の数」と「分割後の曲線の端点」の総和は左側が5つ(赤4+緑1)、右側が4つ(黄色3+緑1)となります。
これを先ほどの考えて同じように「端から順に分割していく」という処理にするには「分割後の曲線と交差する点の数」と「分割後の曲線の端点」の総和が「2」となる曲線を採用していけばいいという訳です
たとえば上図のように⑥で分割した場合、交差点と端点の総和が2となる場合、そちら側の曲線は採用して形状として作成します。
そのときにあわせて反対側に分割した曲線も一緒に作成します。
あとはその反対側に分割した曲線に対しても同じ処理をしていけば徐々に分割していくことができ、最終的にすべてをキレイに分割することができます。
サンプルコードではこのような考えで処理が行われています。
データム化する前で処理を止めれば測定用の形状履歴も確認できるので、詳しく理解したい方はそちらもチェックしてみてください。
まとめ
今回は形状セットの面積を測定し基準値以下の場合は色を付けるマクロについての内容でした。
正直なところ、コード解説を読んでもあまり理解できないと思う方も多いと思います。
というのももっと素直で単純なコードでもできるのではないかと疑いたくなるような処理となってしまっているためです。
分割自体はコード解説のところでも紹介した通りAddNewHybridSplitメソッドを使えば作成できるので、もっと簡単な処理で作成できないかいろいろ試してみて下さい。













