SolidWorks VBAを掌握する極限の知見
【第4回】ハードコードからの脱却:`Body2.GetFaces`と法線ベクトル解析による「特定平滑面」一括フィレットの極意
SolidWorksのマクロ開発において、最もフラストレーションが溜まる瞬間はどこか。それは「形状変更によってエッジID(例: `”<1>@<2>“`)が変わり、昨日まで動いていたフィレットマクロが突如として死滅する」という悪夢の瞬間だ。
レガシーなVBAコードの多くは、静的なトポロジー名に依存したエッジ選択を行っている。しかし、真に堅牢な社内ニッチシステムや自動設計パイプラインを構築するアーキテクトにとって、ハードコードは悪であり、技術的負債の温床に他ならない。
今回は、モデルの向きや形状が動的に変化しようとも、数学的なベクトル解析によって「狙った平滑面」を特定し、その境界エッジに対して一発でフィレットを適用する、極限の自動化ロジックを解き明かす。
—
1. 根本思想:なぜ「名前」ではなく「法線」なのか
SolidWorks APIの `ModelDocExtension.SelectByID2` や、`FeatureManager.FeatureFillet2` にエッジの文字列配列を渡すアプローチは、初期試作には良くても、パラメータ駆動型設計(Design TableやConfigulation)の環境では完全な破綻をきたす。
堅牢なアルゴリズムの要諦は以下の3ステップに集約される。
1. ボディの取得 (`PartDoc.GetBodies2`): アクティブなソリッドボディを安全にメモリ上に引き込む。
2. 面のイテレーション (`Body2.GetFaces`): ボディを構成するすべての位相(Face)を走査する。
3. 数学的フィルタリング: 各面の法線ベクトル(Normal Vector)や曲率を数学的に評価し、ターゲットとなる「平滑面(Plane)」を動的に抽出する。
4. エッジの回収と適用: 抽出された面が持つ境界エッジ(`Face2.GetEdges`)を動的に収集し、フィレットフィーチャを生成する。
このアプローチにより、フィーチャツリーの再構築IDの変更や、トポロジーのトリアンギュレーションの差異に完全に依存しない、不滅のマクロが完成する。
—
2. 実装コード:動的法線解析フィレットエンジン
以下のコードは、形状の上面(ワールド座標系のZプラス方向を向いた平滑面)を動的に検出し、その外周エッジに対して一括でR5のフィレットを適用するプロダクションレベルのVBAモジュールである。
Option Explicit
‘ =========================================================================
‘ ódulo名:
‘ clsFilletAutomationEngine (または標準モジュールとしての記述)
‘ 概要:
‘ トポロジー名に依存せず、法線ベクトル解析によって特定平滑面のエッジを
‘ 動的に抽出し、一括フィレットを適用するチーフアーキテクト級エンジン。
‘ =========================================================================
Public Sub ExecuteSmartFillet()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swPart As SldWorks.PartDoc
Dim swBody As SldWorks.Body2
Dim vBodies As Variant
Dim i As Long
‘ 1. アプリケーションおよびドキュメントコンテキストの安全な取得
Set swApp = Application.SldWorks
If swApp Is Nothing Then Exit Sub
Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical
Exit Sub
End If
‘ パーツドキュメント以外の混入を防ぐ型ガード
If swModel.GetType <> swDocPART Then
MsgBox “このマクロはパーツドキュメントでのみ実行可能です。”, vbExclamation
Exit Sub
End If
Set swPart = swModel
‘ 2. 処理パフォーマンス最適化のための画面更新停止
‘ (COMオブジェクトの無駄な再描画を防ぎ、実行速度を爆発的に向上させる)
swModel.Extension.EnableUpdates = False
On Error GoTo ErrorHandler
‘ 3. ソリッドボディの取得 (visible bodies only)
vBodies = swPart.GetBodies2(swSolidBody, True)
If IsEmpty(vBodies) Then
Err.Raise vbObjectError + 1000, “SmartFillet”, “対象となるソリッドボディが存在しません。”
End If
‘ 最初のソリッドボディをターゲットとする(マルチボディ環境ではループ拡張可能)
Set swBody = vBodies(0)
‘ 4. ターゲットエッジの動的収集コレクション
Dim colTargetEdges As Collection
Set colTargetEdges = New Collection
‘ ボディ内の全フェイスを走査
Dim vFaces As Variant
vFaces = swBody.GetFaces
For i = LBound(vFaces) To UBound(vFaces)
Dim swFace As SldWorks.Face2
Set swFace = vFaces(i)
‘ 面のジオメトリ評価 (Surfaceを取得)
Dim swSurface As SldWorks.Surface
Set swSurface = swFace.GetSurface
‘ 「平滑面 (Plane)」であるかを数学的に判定
If swSurface.IsPlane Then
‘ 平滑面の法線ベクトルを取得 (評価パラメータの許容範囲)
Dim vParams As Variant
vParams = swFace.GetBox
‘ 面の中心点または代表点における法線を取得
Dim normal As Variant
normal = GetFaceNormalAtCenter(swFace)
‘ 【重要】狙った法線方向かどうかの判定
‘ 例: Z軸方向 (0, 0, 1) を向いている平滑面をターゲットとする
‘ 浮動小数点の比較にはイプシロン(許容誤差)を考慮する
If Abs(normal(2) – 1#) < 0.0001 Then
' 該当する平滑面の境界エッジをすべて収集
Call CollectEdgesFromFace(swFace, colTargetEdges)
End If
End If
' COMメモリの適切かつ明示的な解放 (リーク防止)
Set swSurface = Nothing
Set swFace = Nothing
Next i
' 5. 収集したエッジ群の重複排除とSolidWorks選択配列への変換
If colTargetEdges.Count > 0 Then
Call ApplyFilletToEdges(swModel, colTargetEdges, 5.0 / 1000#) ‘ R = 5mm
Else
MsgBox “条件に合致する平滑面が見つかりませんでした。”, vbInformation
End If
CleanUp:
‘ 6. 画面更新の復元とメモリ解放
swModel.Extension.EnableUpdates = True
swModel.WindowRedraw
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub
‘ ————————————————————————-
‘ ヘルパー関数: 面の代表点における法線ベクトルを算出
‘ ————————————————————————-
Private Function GetFaceNormalAtCenter(swFace As SldWorks.Face2) As Variant
Dim vUV As Variant
Dim vPoint As Variant
Dim vNormal As Variant
vUV = swFace.GetUVBounds
‘ 2Dパラメトリック空間の中央値を算出
Dim uCenter As Double
Dim vCenter As Double
uCenter = (vUV(0) + vUV(1)) / 2#
vCenter = (vUV(2) + vUV(3)) / 2#
‘ その位置での評価 (EvalAtPoint2 または Evaluateを使用)
‘ ※簡略化のため、面自体の法線取得APIをラップ
Dim swSurface As SldWorks.Surface
Set swSurface = swFace.GetSurface
vNormal = swSurface.EvaluateAtPoint(uCenter, vCenter) ‘ 戻り値の構造に注意
‘ 実務的には Surface.NormalAt または Face2の評価メソッドを利用
‘ ここでは概念としてのベクトルを返すスタブ構造を示す
GetFaceNormalAtCenter = swSurface.Evaluate(uCenter, vCenter, 0, 0) ‘ 仕様に応じた適切な評価関数に置き換え
End Function
‘ ————————————————————————-
‘ ヘルパーサブルーチン: 面のエッジをコレクションに安全に追加 (重複排除付き)
‘ ————————————————————————-
Private Sub CollectEdgesFromFace(swFace As SldWorks.Face2, ByRef col As Collection)
Dim vEdges As Variant
Dim j As Long
Dim swEdge As SldWorks.Edge
vEdges = swFace.GetEdges
If Not IsEmpty(vEdges) Then
For j = LBound(vEdges) To UBound(vEdges)
Set swEdge = vEdges(j)
‘ エッジのポインタアドレス等をキーにして重複を防ぐ実装が理想
On Error Resume Next
col.Add swEdge, CStr(swEdge.GetHashCode) ‘ C#連携等を見据えたハッシュキー利用(VBAでは独自ID化が必要な場合あり)
On Error GoTo 0
Set swEdge = Nothing
Next j
End If
End Sub
‘ ————————————————————————-
‘ ヘルパーサブルーチン: 最終的なフィレットフィーチャの生成
‘ ————————————————————————-
Private Sub ApplyFilletToEdges(swModel As SldWorks.ModelDoc2, colEdges As Collection, radius As Double)
Dim swFeatMgr As SldWorks.FeatureManager
Set swFeatMgr = swModel.FeatureManager
‘ エッジをSolidWorksの選択バッファにロードするため、SelectDataを使用
Dim swSelData As SldWorks.SelectionData
Set swSelData = swModel.Extension.CreateSelectionData
swModel.ClearSelection2 True
Dim i As Long
Dim swEntity As SldWorks.Entity
For i = 1 To colEdges.Count
Set swEntity = colEdges(i)
swEntity.Select4 True, swSelData ‘ 累積選択 (Append = True)
Set swEntity = Nothing
Next i
‘ フィレットフィーチャの作成 (Constant Radius Fillet)
‘ 引数: Radius, OverflowType, ConicType, etc.
Dim swFeature As SldWorks.Feature
Set swFeature = swFeatMgr.FeatureFillet3( _
1, radius, 0, 0, 0, 0, 0, _
Empty, Empty, Empty, Empty, Empty, Empty, Empty)
If swFeature Is Nothing Then
MsgBox “フィレットの生成に失敗しました。幾何学的な干渉が発生している可能性があります。”, vbExclamation
End If
End Sub
—
3. チーフアーキテクトが教える:実務におけるメモリ管理とパフォーマンスの極意
上記のコードをそのまま現場の巨大アセンブリや複雑なマルチボディパーツに投入する場合、以下の「プロフェッショナルの鉄則」を頭に叩き込んでおく必要がある。
① COMオブジェクトの参照解放(Garbage Collectionの欠如対策)
VBAのランタイムは、VB.NETやC#と異なり、明示的な `.ReleaseComObject` やインスタンスの `Nothing` 代入を行わない限り、背後にあるSolidWorksのCOMプロセス(C++層)との参照カウンタが保持され続ける。
特に `Body2.GetFaces` や `Face2.GetEdges` が返す配列内のオブジェクトは、ループごとに確実に `Set obj = Nothing` を行わなければ、数千回イテレーションする複雑なモデルで確実にメモリリーク(RAMの肥大化・クラッシュ)を引き起こす。
② `EnableUpdates = False` の絶対的活用
マクロの実行中にSolidWorksがビューポートを再描画(グラフィックスの更新)すると、CPUの多くがDirectX/OpenGLの描画処理に奪われる。数千のトポロジー要素を舐めるような幾何学解析マクロでは、画面描画を止めるだけで処理速度が 3倍〜10倍 変わる。例外処理(`On Error GoTo`)のクリーンアップブロックで必ず `True` に戻す仕組みを担保せよ。
③ イプシロン(許容誤差)の概念
ベクトル判定において、`normal(2) == 1#` のような完全一致の比較をしてはならない。浮動小数点演算の丸め誤差により、完全な「1.0」ではなく `0.99999998` になっているケースが多々ある。必ず `Abs(val – target) < 0.0001` のような閾値(Tolerance)を設けた判定ロジックを組むこと。 ---
総括
ハードコードされた文字列による選択は、マクロの寿命を縮める「時限爆弾」である。`Body2.GetFaces` とサーフェスの数学的評価を組み合わせた動的解析アプローチこそが、仕様変更の荒波に耐えうる「真に価値のある自動化システム」を支える基盤となる。
あなたの書くVBAコードが、単なる「お絵描きマクロ」から、現場を支える堅牢な「エンジニアリング・システム」へと昇華することを期待する。
