DXFのペーパー空間には、図枠、表題欄、注記、寸法などを直接配置できます。しかし、モデル空間に描いた機械や配管をシートへ表示するには、モデル空間をのぞく「窓」が必要です。
この窓を表すのがVIEWPORTエンティティです。
VIEWPORTは単なる長方形ではありません。ペーパー空間上の位置と大きさに加えて、モデル空間のどこを見るか、どの範囲を表示するか、視線をどちらへ向けるか、どの画層を非表示にするかといった情報を保持します。help.autodesk.com
この記事では、AutodeskのDXF仕様を基準に、VIEWPORTの主要グループコード、表示尺度の求め方、VPORTテーブルとの違い、非矩形クリッピング、Pythonでの構造化方法を整理します。
VIEWPORTはペーパー空間からモデル空間を見る窓
VIEWPORTには、異なる二つの空間に属する情報が同居します。
- ペーパー空間上の窓の位置と大きさ
- モデル空間側の表示中心、表示高さ、視線方向
ペーパー空間側の代表値は次のとおりです。
| コード | 意味 | 単位・形式 |
|---|---|---|
| 10・20・30 | 窓の中心位置 | ペーパー空間座標 |
| 40 | 窓の幅 | ペーパー空間単位 |
| 41 | 窓の高さ | ペーパー空間単位 |
| 68 | 表示状態と重なり順 | 整数 |
| 69 | Viewport ID | 整数 |
モデル空間側の代表値は次のとおりです。
| コード | 意味 | 単位・形式 |
|---|---|---|
| 12・22 | 表示中心 | モデル空間側の2D点 |
| 16・26・36 | 視線方向 | WCSベクトル |
| 17・27・37 | 注視点 | WCS座標 |
| 45 | 表示高さ | モデル空間単位 |
| 51 | ビューのねじれ角 | 角度 |
コード10の中心はVIEWPORT枠をシートのどこへ置くかを決めます。コード12の中心は、その窓を通してモデル空間のどこを表示するかを決めます。
この二つを同じ座標として扱うと、枠の配置とモデルの表示位置が混同されます。
VIEWPORTとVPORTテーブルは別物
DXFには名前がよく似たVIEWPORTとVPORTがあります。
| 項目 | VIEWPORT | VPORT |
|---|---|---|
| 種類 | 図形エンティティ | シンボルテーブルレコード |
| 主な用途 | ペーパー空間からモデル空間を表示 | モデル空間の画面表示構成を保存 |
| 主な場所 | ENTITIESまたはペーパー空間ブロック | TABLESセクション |
| 複数シートとの関係 | 各レイアウトに配置される | ペーパー空間の窓ではない |
ペーパー空間の縮尺付き窓を解析したい場合は、VIEWPORTエンティティを探します。VPORTテーブルだけを読んでも、各シートに配置されたビューポートは復元できません。ezdxf 1.4.4 documentation
表示尺度はコード41と45の比で決まる
Autodeskの仕様では、ZOOM XPに相当する尺度係数を、ペーパー空間側の高さとモデル空間側の表示高さの比で求めます。help.autodesk.com\[ s = \frac{H_p}{H_m} \]
ここで、
- $H_p$:コード41のVIEWPORT高さ
- $H_m$:コード45のモデル空間表示高さ
- $s$:モデル空間からペーパー空間への尺度係数
です。
モデル空間とペーパー空間を同じ長さ単位で扱う場合、尺度の分母$n$は次のように求められます。\[ n = \frac{H_m}{H_p} \]
尺度の計算例
ペーパー空間上のVIEWPORT高さが120 mm、モデル空間の表示高さが2,400 mmなら、\[ s = \frac{120}{2400}=0.05 \]\[ n = \frac{2400}{120}=20 \]
したがって、表示尺度は1:20です。
ただし、モデル空間がmm、ペーパー空間がinchのように単位系が異なる場合は、先に同じ物理単位へ換算しなければなりません。コード41と45をそのまま割った値だけで尺度表記を確定しないようにします。
表示幅はアスペクト比から求める
VIEWPORT枠のアスペクト比は、コード40と41から求められます。\[ r = \frac{W_p}{H_p} \]
平行投影で特殊なクリッピングがなく、枠全体を使う単純な場合、モデル空間側の表示幅は、\[ W_m = H_m \frac{W_p}{H_p} \]
です。
たとえば枠幅180 mm、枠高さ120 mm、モデル表示高さ2,400 mmなら、\[ W_m = 2400\times\frac{180}{120}=3600\ \mathrm{mm} \]
となります。
表示中心が$(x_c,y_c)$なら、回転や透視投影を無視した概算表示範囲は、\[ x_{min}=x_c-\frac{W_m}{2},\quad x_{max}=x_c+\frac{W_m}{2} \]\[ y_{min}=y_c-\frac{H_m}{2},\quad y_{max}=y_c+\frac{H_m}{2} \]
です。
ビューのねじれ角、視線方向、透視投影、非矩形クリッピングが有効な場合は、この軸平行矩形だけでは正確な表示領域になりません。
状態コード68とフラグ90を分けて読む
コード68はVIEWPORTの表示状態と重なり順を表します。
| 値 | 意味 |
|---|---|
| -1 | オンだが画面外、または最大アクティブ数超過で非アクティブ |
| 0 | オフ |
| 正の値 | オンかつアクティブ。値は重なり順に関係 |
一方、コード90はビットフラグです。代表的な値には次があります。
| ビット値 | 意味 |
|---|---|
| 1 | 透視投影を有効化 |
| 2 | 前方クリッピングを有効化 |
| 4 | 後方クリッピングを有効化 |
| 2048 | 隠線処理を有効化 |
| 16384 | ビューポート尺度をロック |
| 65536 | 非矩形クリッピングを有効化 |
| 131072 | ビューポートをオフ |
複数の機能が同時に有効になるため、コード90を単一の列挙値として比較してはいけません。
LOCK_ZOOM = 16384NON_RECTANGULAR_CLIP = 65536TURN_VIEWPORT_OFF = 131072def has_flag(value: int, flag: int) -> bool: return (value & flag) != 0
非矩形クリッピングは境界図形を参照する
長方形以外の形でモデル空間を表示するVIEWPORTでは、コード90の非矩形クリッピングフラグが立ち、コード340に境界図形へのハードポインタが入ります。help.autodesk.com
解析手順は次のとおりです。
- コード90で非矩形クリッピングの有無を判定する
- コード340から境界図形のハンドルを取得する
- ハンドル索引で境界図形を解決する
- 境界形状をペーパー空間座標として復元する
境界参照が切れている場合、長方形へ勝手に置き換えると表示結果が変わります。参照元VIEWPORT、コード340の値、解決失敗を診断情報として残し、修復は別工程にします。
ビューポートごとの画層凍結
コード331には、そのVIEWPORT内だけで凍結するLAYERオブジェクトのハンドルが入ります。コード331は複数回出現できます。
したがって、次のような単純辞書では最後の1件しか残りません。
data[331] = value
複数値として保持し、LAYERテーブルのハンドル索引へ解決します。
frozen_layer_handles = [ value for code, value in tags if code == 331]
画層名だけを保存するより、元ハンドルと解決後の画層名を両方残すと、参照切れや重複を検査しやすくなります。
PythonでVIEWPORTを構造化する
主要コードを抽出し、尺度と単純なモデル表示範囲を計算する例です。
from dataclasses import dataclass@dataclassclass ViewportData: paper_center: tuple[float, float] paper_width: float paper_height: float model_center: tuple[float, float] model_view_height: float status: int flags: int frozen_layer_handles: list[str] clipping_boundary_handle: str | None def scale_factor(self) -> float: if self.model_view_height <= 0: raise ValueError( "model_view_height must be > 0" ) return ( self.paper_height / self.model_view_height ) def model_limits( self, ) -> tuple[float, float, float, float]: if self.paper_height <= 0: raise ValueError( "paper_height must be > 0" ) model_width = ( self.model_view_height * self.paper_width
このmodel_limits()は、平行投影、ねじれ角0、長方形クリップという単純条件での概算です。一般的なレンダラーでは、視線方向、注視点、ねじれ角、透視投影、クリッピング境界を含む変換行列へ進めます。
実装で起こりやすい失敗
コード10と12を同じ中心点として扱う。
10はペーパー空間上の枠配置、12はモデル空間側の表示中心として分離します。
VPORTテーブルをペーパー空間の窓として読む。
ペーパー空間の表示窓はVIEWPORTエンティティです。
尺度値が直接保存されていると考える。
基本尺度はコード41と45の比から求めます。単位系も確認します。
コード331を1件だけ保存する。
ビューポート固有の凍結画層は複数存在できます。
コード90を通常の状態番号として読む。
ビット演算で各機能を個別に判定します。
まとめ
DXFのVIEWPORTは、ペーパー空間上の窓とモデル空間の表示条件を結び付けるエンティティです。
基本的な対応は次のとおりです。
- コード10・20:ペーパー空間上の中心
- コード40・41:ペーパー空間上の幅と高さ
- コード12・22:モデル空間側の表示中心
- コード45:モデル空間側の表示高さ
- コード68:表示状態と重なり順
- コード90:透視投影、尺度ロック、非矩形クリップなどのビットフラグ
- コード331:VIEWPORT内で凍結する画層
- コード340:非矩形クリッピング境界への参照
尺度係数は、\[ s = \frac{H_p}{H_m} \]
で求めます。
VIEWPORTを構造化できれば、シートごとの表示範囲、尺度、視線方向、画層状態、クリッピング境界を比較できます。さらに、縮尺表記との整合確認、図面セットの自動検査、Web DXFビューア、レイアウト差分検出へ発展させられます。

