【実務・中級編】滑らかな自由曲線の自動生成:Shape.DrawSplineとDrawNURBSの制御アルゴリズム – Visio VBA解析バイブル

スポンサーリンク

Visio VBAを掌握する極限の知見:滑らかな自由曲線の自動生成(DrawSpline / DrawNURBS)

業務自動化エンジニアの皆さん、こんにちは。
これまで数々のVisioマクロや大型の図面自動生成システムを構築してきた中で、最も多くの開発者が挫折し、そして最も美しい成果物を生み出せる領域、それが「座標配列からの自由曲線の動的生成」です。

配管ルートのシミュレーション、ネットワークのフロー、あるいはセンサーデータの時系列プロット。直線や四角形を並べるだけの幼児的な図面作成から脱却し、「滑らかな曲線(Spline / NURBS)」をコードから意図通りに制御する技術は、実務レベルの業務自動化ツールにおいて決定的な差別化要素となります。

今回は、`Shape.DrawSpline` と `Shape.DrawNURBS` の本質を解き明かし、現場で即座に使える堅牢なプロダクションコードを伝授します。

なぜ、安易な `DrawSpline` では実務で使い物にならないのか?

多くの開発者は、ネット上の断片的なコードを拾い集め、以下のようなコードを書いて絶望します。

‘ 【アンチパターン】よくある素朴な実装
Dim xy(5) As Double
xy(0) = 0: xy(1) = 0
xy(25) = 2: xy(3) = 5
‘ …配列に座標を詰め込んでDrawSplineを呼ぶ
ActivePage.DrawSpline xy

なぜこれが実務で通用しないのか。理由は3つあります。

1. Visio特有の座標系の罠(インチ系と内部単位)
VisioのVBAエンジンは、UI上の単位(mmやcm)に関わらず、内部的には「インチ(Inches)」で計算を行います。これを無視して生データをぶち込むと、図面がミクロサイズになるか、宇宙のように巨大化します。
2. 配列の次元数とデータの方向の厳格さ
`DrawSpline` や `DrawNURBS` が要求する `Double` 型の1次元配列の構造は非常にシビアです。インデックスの順序を誤ると、Visioは容赦なくエラー `-203.11`(無効な引数)を吐いて沈黙します。
3. NURBSの数学的複雑性
`DrawNURBS`(Non-Uniform Rational B-Spline)は、制御点(Control Points)だけでなく、ノットベクトル(Knot Vector)や重み(Weight)を正確に制御しなければ、意図した形状になりません。

プロのエンジニアとして、これらのブラックボックスを完全制御下に置くアプローチを解説します。

実務で使える堅牢なアーキテクチャ設計

プロダクション環境で動くコードに必要なのは、以下の3要素です。

  • 入力データのバリデーション: 座標点数が足りているか、配列が正しく初期化されているか。
  • 単位変換のカプセル化: mm等の外部単位を、Visioが要求するInternal Units(インチ)へ確実に変換する。
  • トランザクション管理: 描画処理中にエラーが発生した場合でも、図面が中途半端な状態でフリーズしないよう `Application.ScreenUpdating` やエラーハンドリングを適切に行う。

では、実際にコピペして実務で即座に動かせるモジュールを公開します。

プロダクションコード:高精度スプライン曲線自動生成エンジン

以下のコードをVisioの標準モジュールに貼り付けてください。CSVやデータベースから取得した座標の配列を想定し、滑らかなスプライン曲線を描画します。

Option Explicit

‘ ==============================================================================
‘ モジュール名: ModCurveGenerator
‘ 概要 : 座標配列からVisio上に滑らかなスプライン/NURBS曲線を動的生成する
‘ 著者 : 業務自動化チーフアーキテクト
‘ ==============================================================================

Public Sub CreateSmoothSplineSample()
‘ 1. サンプル用の座標データ(X, Yのペアをミリメートル単位で定義)
‘ 実務では、DBやExcel、外部CSVからこの配列を動的に構築してください。
Dim rawPoints() As Double
ReofRawPoints rawPoints

‘ 2. エラーハンドリングとパフォーマンス最適化の盾を構える
On Error GoTo ErrorHandler
Application.ScreenUpdating = False
Application.UndoEnabled = False

Dim targetPage As Visio.Page
Set targetPage = ActivePage

‘ 3. スプライン曲線の描画実行
Dim shpSpline As Visio.Shape
Set shpSpline = DrawSplineFromMillimeters(targetPage, rawPoints)

‘ 4. 図形のプロパティ調整(実用的な装飾)
If Not shpSpline Is Nothing Then
shpSpline.CellsSRC(visSectionObject, visRowLine, visLineColor).FormulaU = “RGB(0, 120, 215)” ‘ プロフェッショナルな青
shpSpline.CellsSRC(visSectionObject, visRowLine, visLineWeight).FormulaU = “1.5 pt” ‘ 視認性の高い太さ
shpSpline.Text = “自動生成ルート #1”
End If

CleanUp:
Application.ScreenUpdating = True
Application.UndoEnabled = True
Exit Sub

ErrorHandler:
MsgBox “曲線の自動生成中に致命的なエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“詳細: ” & Err.Description, vbCritical, “VisioAutomation Error”
Resume CleanUp
End Sub

‘ ——————————————————————————
‘ 内部関数: ミリメートル単位の座標配列をVisio内部単位(インチ)に変換し、DrawSplineを実行
‘ ——————————————————————————
Private Function DrawSplineFromMillimeters(pg As Visio.Page, mmPoints() As Double) As Visio.Shape
Dim i As Long
pCount As Long

‘ 座標のペア数(XとYで1セット)を算出
Dim numCoords As Long
numCoords = UBound(mmPoints) – LBound(mmPoints) + 1

If numCoords < 4 Or (numCoords Mod 2 <> 0) Then
Err.Raise 9999, , “座標データの形式が不正です。X,Yのペアで最低2点(計4要素以上)が必要です。”
End If

‘ VisioのDrawSplineメソッドは「1次元のDouble型配列(X0, Y0, X1, Y1…)」を要求する
‘ すべての数値をmmからインチ(Inches)へ変換する
Dim visioCoords() As Double
ReDim visioCoords(LBound(mmPoints) To UBound(mmPoints))

For i = LBound(mmPoints) To UBound(mmPoints) Step 2
‘ mmをインチに変換 (1インチ = 25.4 mm)
‘ ついでにVisioの原点計算(必要に応じた反転など)もここで吸収可能
visioCoords(i) = mmPoints(i) / 25.4 ‘ X座標
visioCoords(i + 1) = mmPoints(i + 1) / 25.4 ‘ Y座標
Next i

‘ Visio API: DrawSplineの呼び出し
‘ 引数: 座標配列
Dim resultShape As Visio.Shape
Set resultShape = pg.DrawSpline(visioCoords)

Set DrawSplineFromMillimeters = resultShape
End Function

‘ ——————————————————————————
‘ テスト用データ生成ヘルパー
‘ ——————————————————————————
Private Sub ReofRawPoints(ByRef pts() As Double)
‘ 座標のサンプル(X1, Y1, X2, Y2, … Xn, Yn)単位: mm
‘ 緩やかなウェーブを描くようにポイントを配置
Dim temp As Variant
temp = Array( _
10#, 10#, _
30#, 50#, _
60#, 20#, _
90#, 70#, _
120#, 40# _
)

ReDim pts(LBound(temp) To UBound(temp))
Dim i As Long
For i = LBound(temp) To UBound(temp)
pts(i) = CDbl(temp(i))
Next i
End Sub

さらに高度な制御:`DrawNURBS` を使いこなす

もしあなたが「通過点を正確に通りつつ、曲線のテンション(張力)や接線を完全にコントロールしたい」という要件に直面しているなら、`DrawSpline` ではなく `DrawNURBS` を選択すべきです。

`DrawNURBS` のシグネチャは以下のようになっています:
`expression.DrawNURBS(xyArray, degree, knotVector, weightVector)`

  • degree(次数): 通常は 3(三次スプライン)を指定するのが最も自然な滑らかさを得られます。
  • knotVector(ノットベクトル): 制御点の影響範囲を決定する配列。これを適切に計算しないと、曲線がねじれます。
  • weightVector(重み配列): 各制御点の「引きつける力」。通常はすべて `1.0` で初期化しますが、特定の点だけ磁力を強くするような表現が可能です。

実務上、複雑なCADデータや外部の数学ライブラリ(Python等)と連携してVisio図面を自動生成する場合、Python側でNURBSのノットを計算し、VBAにJSONやCSV経由で渡して `DrawNURBS` をキックするアーキテクチャが非常に堅牢です。

開発現場への導入とパフォーマンスの鉄則

1. 画面描画の凍結は絶対の義務
大量の配管やセンサー軌跡を一度に何百本も描画する場合、`Application.ScreenUpdating = False` を忘れると、Visioが毎回の描画と再計算(レイアウト再計算)走り、処理が数十倍遅くなります。
2. Undoスタックの管理
自動化スクリプトが数千個の図形を操作するとき、Undo(元に戻す)の履歴がメモリを圧迫し、最悪の場合 Out of Memory(メモリ不足)でVisioがクラッシュします。大規模バッチ処理では `Application.UndoEnabled = False` を必ず挟んでください。

最後に

「コードで線を引く」ということは、単にマウス操作を代替するだけではありません。「データの規則性を視覚的な美しさにリアルタイムで変換する」という、エンジニアリングの醍醐味そのものです。

今回解説した座標変換のラップ手法と堅牢なエラーハンドリングをあなたのプロジェクトに組み込めば、どんなに複雑な曲線描画の要求が来ようとも、微動だにしないプロフェッショナルな自動化ツールを構築できるはずです。

現場の生産性を極限まで引き上げるコードを、ぜひあなたのソリューションに実装してください。

タイトルとURLをコピーしました