【SolidWorks VBA極限解説】Body2.GetFacesを駆使した特定平面交差エッジの自動フィルタリングとスマート・フィレット生成
SolidWorksのマクロ開発において、最もフラストレーションが溜まる瞬間はどこか。それは「幾何公差や設計変更に追随しない、ハードコードされたエッジID(Edge ID)に依存したフィーチャ生成の破綻」に他ならない。
特に、モデル全体に対してではなく「特定の平面に隣接し、かつその平面と交差するエッジ群」に対して動的にフィレット(角丸め)を適用したい場合、単純な全エッジの列挙では意図しないエッジを巻き込み、APIエラー(戻り値の失敗)を引き起こす。
今回は、`Body2.GetFaces`から得られる位相幾何学(トポロジー)の情報を完全に掌握し、メモリリークを極限まで排除しながら、特定の基準平面に接するエッジ群のみを数学的・位相的に自動フィルタリングしてフィレットを生成する、実務レベルのコードを公開する。
—
1. 幾何学的選択の罠とトポロジー解析の必要性
SolidWorks APIでフィレット(`FeatureManager::InsertFeatureFillet`)をかける際、最も避けるべきは「固定されたエッジ名」の指定である。パラメータ変更によりトポロジーが再計算(Rebuild)された瞬間、エッジの内部IDは書き換わり、マクロは沈黙する。
シニアエンジニアが実装すべきアプローチは以下の通りだ。
1. 基準となる平面(Face)の動的特定(例:原点からの距離や法線ベクトル、あるいは属性による取得)。
2. `Body2.GetFaces`による全面(Face2)の走査。
3. 対象平面と隣接(共有エッジを持つ)する面、または対象平面そのものを構成する境界エッジの抽出。
4. COMオブジェクトの適切な解放(Marshal.ReleaseComObject相当のVBAでの対策)。
特にVBA環境では、COMラッパーの参照カウントが適切に管理されないと、SolidWorksのプロセス内にゾンビオブジェクトが残り、メモリリークや突然のクラッシュの原因となる。ここではその点も徹底的に考慮する。
—
2. 実装コード:スマート・フィレット自動生成エンジン
以下のコードは、アクティブなパーツドキュメントにおいて、指定した平面(例:初期フィーチャの「上面」など)に隣接するエッジを動的に抽出し、安全にフィレットを適用する実用プロシージャである。
Option Explicit
‘ ——————————————————————————–宗派
‘ 固有定数定義
‘ ——————————————————————————–
Private Const swSelFACES As Long = 1
Private Const swSelEDGES As Long = 2
Private Const swEntity As Long = 0
Sub ExecuteSmartFillet()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swPart As SldWorks.PartDoc
Dim swBody As SldWorks.Body2
Dim swBodyArr As Variant
Dim swFace As SldWorks.Face2
Dim swEdge As SldWorks.Edge
Dim vFaces As Variant
Dim vEdges As Variant
Dim vConnectedFaces As Variant
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then
MsgBox “ドキュメントが開かれていません。”, vbCritical
Exit Sub
End If
If swModel.GetType <> 1 Then ‘ swDocPART
ValueOfError “このマクロはパーツドキュメントでのみ実行可能です。”
Exit Sub
End If
Set swPart = swModel
‘ 1. アクティブボディの取得(マルチボディ対応の際は評価ロジックを拡張)
swBodyArr = swPart.GetBodies2(swSelFACES, False)
If IsEmpty(swBodyArr) Then Exit Sub
Set swBody = swBodyArr(0)
‘ 2. 処理対象となる基準平面の特定(ここでは例として最初のPlanar面、または特定の名前の面を探索)
Set swFace = FindTargetPlanarFace(swBody, “上視方向”) ‘ 独自関数で特定
If swFace Is Nothing Then
MsgBox “対象となる基準平面が見つかりませんでした。”, vbExclamation
Exit Sub
End If
‘ 3. 基準平面を構成する(または隣接する)エッジ群のフィルタリング抽出
‘ Face2::GetEdges は、その面を境界として持つすべてのEdgeオブジェクトの配列を返す
vEdges = swFace.GetEdges
If IsEmpty(vEdges) Then
MsgBox “対象面に関連するエッジが存在しません。”, vbExclamation
Exit Sub
End If
‘ 4. 選択バッファのクリアとエッジの動的選択
swModel.ClearSelection2 True
Dim i As Long
Dim swEntityObj As SldWorks.Entity
Dim successCount As Long
successCount = 0
For i = LBound(vEdges) To UBound(vEdges)
Set swEdge = vEdges(i)
Set swEntityObj = swEdge
‘ エッジをマーク付加して選択(Append = True)
‘ 第4引数: 1 = 選択マーク(フィレットフィーチャ作成時に必要)
If swEntityObj.Select4(True, 1, False) Then
successCount = successCount + 1
End If
‘ ループ内でのメモリ解放(VBAのポインタ肥大化を防ぐ最重要作法)
Set swEntityObj = Nothing
Set swEdge = Nothing
Next i
If successCount = 0 Then
MsgBox “フィルタリングされたエッジの選択に失敗しました。”, vbCritical
GoTo CleanUp
End If
‘ 5. フィレットフィーチャの実行
‘ 引数: 半径, 伝播フラグ, 保持タイプ等
Dim swFeat As SldWorks.Feature
Set swFeat = swModel.FeatureManager.InsertFeatureFillet( _
1, 3, 0, 0, _
0.005, 0.005, 0.005, _
False, False, False, False, False, False, False)
If Not swFeat Is Nothing Then
swModel.GraphicsRedraw2
MsgBox “スマート・フィレットの生成が完了しました。(適用エッジ数: ” & successCount & “)”, vbInformation
Else
MsgBox “フィレットフィーチャの生成に失敗しました。幾何学的な干渉を確認してください。”, vbCritical
End If
CleanUp:
‘ 6. 明示的なオブジェクト解放(メモリ最適化)
Set swFace = Nothing
Set swBody = Nothing
Set swPart = Nothing
Set swModel = Nothing
Set swApp = Nothing
End Sub
‘ ——————————————————————————–
‘ 補助関数: 特定の条件(例:法線がZ軸方向を向く平面)を満たすFace2を探索
‘ ——————————————————————————–
Private Function FindTargetPlanarFace(ByVal swBody As SldWorks.Body2) As SldWorks.Face2
Dim vFaces As Variant
Dim swFace As SldWorks.Face2
Dim swSurface As SldWorks.Surface
Dim vParam As Variant
vFaces = swBody.GetFaces
Dim i As Long
For i = LBound(vFaces) To UBound(vFaces)
Set swFace = vFaces(i)
Set swSurface = swFace.GetSurface
‘ 平面(Planar Surface)であるか判定
If swSurface.IsPlane() Then
vParam = swSurface.PlaneParams
‘ vParam(0..2) = 法線ベクトル(nx, ny, nz), vParam(3..5) = 通過点(x, y, z)など
‘ ここでは例として、Z方向を向いた平面(上向きの面)をキャッチする
If Abs(vParam(2) – 1#) < 0.0001 Then
Set FindTargetPlanarFace = swFace
Exit Function
End If
End If
Set swFace = Nothing
Next i
Set FindTargetPlanarFace = Nothing
End Function
Private Sub ValueOfError(ByVal msg As String)
MsgBox msg, vbCritical, "Architecture Error"
End Sub
---
3. チーフアーキテクトが解説するコードの急所とメモリ最適化
上記のコードは一見して標準的なVBAに見えるが、大規規模なCADアセンブリや複雑なマルチボディパーツにおいて破綻しないよう、以下の極限の知見が組み込まれている。
① `Face2::GetEdges` の破壊力と位相の方向性
`Face2::GetEdges` は、その面を取り囲む境界エッジの配列をダイレクトに取得する。モデル全体から `Body2.GetEdges` で数千本のエッジを取得して総当たりで判定するアプローチと比較し、計算量はO(N)からO(K)(Kは面を構成するエッジ数)へと劇的に軽減される。これにより、処理速度は数倍〜数十倍に跳ね上がる。
② ループ内でのCOMポインタの粉砕(Memory Leak Mitigation)
VBAは内部でCOMコンポーネントの参照カウントを自動管理している「ように見えて」、複雑なAPIメソッド(特に `GetFaces` や `GetEdges` が返すバリアント配列内のCOMオブジェクト)は、明示的にローカル変数を `Nothing` にクリアしないと、VBAランタイムのメモリ空間に参照が残り続ける。
特にループ内で `Set swEdge = vEdges(i)` のように代入を繰り返す場合、古いポインタがガベージコレクションされずに蓄積し、数百回のエッジ処理でメモリ不足(Out of Memory)エラーを引き起こす。コード内で `Set swEntityObj = Nothing` および `Set swEdge = Nothing` をループの都度実行しているのは、このハードコアなメモリリークを防ぐための防壁である。
③ 選択マーク(Selection Mark)の正確な制御
`swEntityObj.Select4(True, 1, False)` の第2引数 `1` は非常に重要だ。
SolidWorksのフィレットAPI(`InsertFeatureFillet`)は、選択されたエンティティが「どの目的で選択されたか」をマーク値によって判別している。エッジフィレットの場合、通常マーク `1` が期待されるため、この指定を誤るとAPIがエッジを認識せずエラーを返す。レガシーコードによくある「なぜか動くがパラメータを変えると死ぬ」原因のほとんどは、この選択マークの不整合にある。
—
4. レガシー環境・システム間連携への拡張
このロジックをベースに、さらに外部システム(生産管理MESやERP、あるいはWebフロントエンド)からのJSON/XML指示と連携させる場合、以下のように拡張するのがシニアエンジニアの作法である。
- パラメータの外部化: フィレット半径(`0.005`の部分)や対象面の法線条件を、JSONファイルから動的に読み込ませる。
- トランザクション制御: 万が一フィーチャ生成に失敗した場合、`ModelDoc2::Extension.Rebuild` の結果を検証し、失敗時は直前の状態までロールバック(あるいはUndo処理のプログラム的実行)を挟む。
設計変更に強く、メモリを溺れさせない堅牢なSolidWorksマクロ。このアーキテクチャをあなたの開発パイプラインに組み込んでほしい。
