「マクロの記録」のその先へ。図面を美しく整える「寸法線レイアウト最適化」の思考法
こんにちは。設計自動化の世界へようこそ。
あなたが今、SolidWorks VBAを学んでいて、「マクロの記録」ボタンを連打してコードを生成しているとしたら……。それは素晴らしい第一歩です。しかし、同時に「自動化の壁」に直面しているはずです。
「記録したコードを動かしてみたけれど、別の図面では全く同じ動きをしてくれない」
「寸法が重なってしまい、結局手動で直している」
もしそう感じているなら、おめでとうございます。あなたは今、「操作を記録する人」から「システムを設計するエンジニア」へと進化する境界線に立っています。
今日は、実務で最も泥臭く、かつ最も価値が高いテーマの一つである「図面内の寸法線レイアウトの自動整列」について、その設計思想と実装の極意を伝授します。
1. なぜ「マクロの記録」では寸法を整列させられないのか?
「マクロの記録」が生成するコードの多くは、`SelectByID2`(名前を指定して選択する)や、特定の座標への移動(`SetPosition`)で構成されています。
これは、いわば「決め打ち」のプログラミングです。
「Aという寸法の位置を、10mm上に動かす」という命令は書けますが、「もし寸法同士が重なっていたら、重ならない位置まで動かす」という「判断」は、記録されたコードには書けないのです。
図面を美しく整列させるためには、以下の3つのステップをプログラムに持たせる必要があります。
1. 検知 (Detection): 図面内のすべての寸法オブジェクトをスキャンし、それぞれの「位置」と「サイズ(占有範囲)」を把握する。
2. 判定 (Collision Check): 二つの寸法の占有範囲が重なっているかを、数学的に計算する。
3. 解決 (Resolution): 重なりを解消するために、適切なオフセット量(隙間)を計算して、寸法を再配置する。
ここからは、このロジックをどう実装していくか、エンジニアの思考プロセスを辿っていきましょう。
2. 核心:寸法オブジェクトの「存在」を捉える
寸法を動かすためには、まず「その寸法が図面のどこに、どのくらいの大きさで存在しているか」を知る必要があります。
ここで重要になるのが、SolidWorks APIのオブジェクト階層です。
図面において、私たちが操作したいのは `DisplayDimension` オブジェクトです。しかし、その位置情報や表示領域(Bounding Box)にアクセスするためには、その背後にある `Annotation` オブジェクトを介する必要があるケースがほとんどです。
考えるべきパラメータ
寸法が重なっているかを判定するために、以下の要素を抽出します。
- $X, Y$ 座標: 寸法テキストの現在の位置。
- 幅 ($W$) と 高さ ($H$): テキストや寸法線が占有している領域の大きさ。
3. 実装例:寸法重なり検知と自動オフセット
それでは、具体的なコードを見ていきましょう。
ここでは、「水平方向に並んでいる寸法同士が、垂直方向に重なっている場合に、上下に隙間を空ける」という、実務で非常に重宝するロジックをベースに解説します。
※このコードは、学習用にロジックを簡略化していますが、プロフェッショナルな設計の雛形としてそのまま活用できます。
Option Explicit
' ==============================================================================
' テーマ: 重なった寸法線の自動整列(垂直方向の衝突回避)
' 概要: 図面内の寸法を走査し、垂直方向の距離が近いものを検知してオフセットする
' ==============================================================================
Sub OptimizeDimensionLayout()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swDraw As SldWorks.DrawingDoc
Dim swSheet As SldWorks.Sheet
Dim swView As SldWorks.View
Dim swDispDim As SldWorks.DisplayDimension
Dim swAnn As SldWorks.Annotation
Dim dimList() As SldWorks.DisplayDimension ' 寸法オブジェクトのリスト
Dim dimPos() As Double ' 寸法のY座標リスト
Dim dimCount As Long
Dim i As Long, j As Long
' 定数設定
Const MIN_GAP As Double = 0.005 ' 最小の隙間 (単位はメートル: 5mm)
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
' 図面ドキュメントかチェック
If swModel Is Nothing Or swModel.GetType <> swDocDRAWING Then
MsgBox "図面ファイルを開いてから実行してください。"
Exit Sub
End If
Set swDraw = swModel
Set swView = swDraw.GetFirstView ' Sheet自体を取得
' --- STEP 1: 図面内のすべての寸法を収集 ---
dimCount = 0
Set swView = swView.GetNextView ' 最初のビュー(シート)の次へ移動
Do While Not swView Is Nothing
Set swDispDim = swView.GetFirstDisplayDimension
Do While Not swDispDim Is Nothing
dimCount = dimCount + 1
ReDim Preserve dimList(1 To dimCount)
ReDim Preserve dimPos(1 To dimCount)
Set dimList(dimCount) = swDispDim
' 注釈オブジェクトから位置を取得
Set swAnn = swDispDim.GetAnnotation
dimPos(dimCount) = swAnn.GetPosition(0, 0, 0, 0) ' Y座標を取得(簡易化のため)
' ※実際にはGetPositionの戻り値から適切なインデックスを抽出
' 本来はここで Y座標を正確に取得するロジックが必要
' 以下、概念的な実装として Y座標を抽出
Dim pos(2) As Double
swAnn.GetPosition pos(0), pos(1), pos(2)
dimPos(dimCount) = pos(1)
Set swDispDim = swDispDim.GetNextDisplayDimension
Loop
Set swView = swView.GetNextView
Loop
If dimCount < 2 Then
MsgBox "比較可能な寸法が見つかりませんでした。"
Exit Sub
End If
' --- STEP 2: 重なり判定とオフセット実行 ---
' 二重ループで全ての組み合わせを比較(計算量に注意)
' 実務では、近接しているものだけに限定する空間分割アルゴリズムを使うのが理想
swModel.DrawingViewUpdate
For i = 1 To dimCount
For j = i + 1 To dimCount
' 垂直方向の距離を計算
Dim diffY As Double
diffY = Abs(dimPos(i) - dimPos(j))
' もし距離が設定した閾値(MIN_GAP)より小さければ、重なっているとみなす
If diffY < MIN_GAP Then
' 重なりを解消するために、j番目の寸法を上に押し上げる
' (iとjのどちらを動かすかは、既存のレイアウト規則に基づき決定する)
Dim swAnnI As SldWorks.Annotation
Dim swAnnJ As SldWorks.Annotation
Set swAnnI = dimList(i).GetAnnotation
Set swAnnJ = dimList(j).GetAnnotation
Dim currentPosI(2) As Double
Dim currentPosJ(2) As Double
swAnnI.GetPosition currentPosI(0), currentPosI(1), currentPosI(2)
swAnnJ.GetPosition currentPosJ(0), currentPosJ(1), currentPosJ(2)
' 新しい位置を計算(iより上に配置)
Dim newY As Double
If currentPosI(1) > currentPosJ(1) Then
newY = currentPosI(1) + MIN_GAP
Else
newY = currentPosJ(1) + MIN_GAP
End If
' 位置を更新
swAnnJ.SetPosition currentPosJ(0), newY, currentPosJ(2)
' 次の判定のために座標リストを更新(重要!)
dimPos(j) = newY
' 画面更新を抑制するために、最後に一括で行うのが理想だが、
' 今回は動作確認のため逐次更新
End If
Next j
Next i
' 描画の再構築
swModel.ForceRebuild3 True
MsgBox "レイアウトの最適化が完了しました。"
End Sub
4. チーフアーキテクトからの「極限の知見」
コードを書き終えて満足してはいけません。プロのエンジニアは、常に「計算コスト」と「堅牢性」を気にかけます。このコードをさらに一段上のレベルへ引き上げるためのアドバイスを贈ります。
① 計算量の罠($O(n^2)$ 問題)
上記のコードでは、寸法を総当たりで比較する「二重ループ」を使用しています。寸法が10個なら100回の比較で済みますが、大規模な図面で寸法が500個ある場合、250,000回の比較が発生します。
これを回避するには、「グリッド分割(Spatial Partitioning)」という手法を使います。図面を格子状のエリアに分け、同じエリア、あるいは隣接するエリアにある寸法同士だけを比較するのです。これだけで、処理速度は劇的に向上します。
② オブジェクトのライフサイクルと更新
`swAnn.SetPosition` を実行した直後、SolidWorks内部の座標系はまだ「古い状態」のままです。
コード内で `dimPos(j) = newY` と手動で配列を更新しているのは、「次に比較する相手に対して、最新の(動かした後の)座標で判定するため」です。ここを怠ると、連続して重なっている寸法を正しく回避できなくなります。
③ 単位の意識
SolidWorks APIの内部単位は、常に メートル (m) です。
「5mm空けたい」と思って `SetPosition 5` と書くと、5メートル空けることになり、寸法は図面の遥か彼方へ飛んでいってしまいます。`0.005` と書くか、定数として `Const MM_TO_M = 0.001` を定義する癖をつけましょう。
最後に
「寸法を自動で並べる」というタスクは、一見すると単純な作業に見えます。しかし、そこには幾何学的な判定、オブジェクトの階層構造の理解、そしてパフォーマンスへの配慮といった、エンジニアリングの本質が詰まっています。
マクロの記録から卒業し、こうした「論理的な思考」をコードに落とし込めるようになれば、あなたの自動化スキルは無敵のものになります。
一歩ずつ、確実に。図面を支配するエンジニアを目指していきましょう。応援していますよ。
