DXFの単位を正しく扱う|$INSUNITS・mm・inchとezdxfの尺度変換

「DXFの単位を正しく扱う|$INSUNITS・mm・inchとezdxfの尺度変換」の内容を表す技術イラスト

DXFをCAD間で受け渡したり、Pythonで自動生成したりすると、ときどき次のような問題が起こります。

  • 開いた図形が極端に大きい
  • 逆に非常に小さい
  • mmで作ったつもりなのにinchとして扱われる
  • ブロックを挿入すると尺度が変わる
  • Pythonで100と書いた長さが「100 mmなのか100 inchなのか」分からない

原因を理解するには、最初にDXFの重要な性質を押さえる必要があります。

DXFの座標値そのものに、mmやinchという単位が直接付いているわけではありません。

たとえばLINEの終点が、

10
100.0
20
50.0

だったとしても、100.0という数値そのものが100 mmなのか100 inchなのかを、この座標値だけから判断することはできません。

DXFでは図形の数値と「その図面を何単位として扱うか」を分けて考える必要があります。

その中心になるHEADER変数が、

$INSUNITS

です。

目次

DXFの長さは基本的にdrawing unitである

DXFを扱うときは、まず「drawing unit」という考え方を持つと理解しやすくなります。

たとえば、

msp.add_line(
    (0, 0),
    (100, 0),
)

という図形を作ったとします。

幾何学的に確実に言えるのは、

この線の長さは100 drawing unitsである

ということです。

100 mmとはまだ限りません。

100 inchでも、100 mでも、単位未指定の100でもあり得ます。

ezdxfの公式ドキュメントも、DXFの長さや座標値は第一義的にはunitlessであり、document/modelspaceの単位情報は$INSUNITSから得られると説明しています。

この性質は、DXFをプログラムで扱うときに非常に重要です。

$INSUNITSとは何か

$INSUNITSはDXFのHEADERセクションに保存される変数です。

AutodeskはINSUNITSを、ブロック、画像、外部参照などを挿入またはアタッチするときの自動尺度調整に使用する作図単位値として定義しています。

代表的な値は次の通りです。

$INSUNITS単位
0Unitless / Unspecified
1Inches
2Feet
4Millimeters
5Centimeters
6Meters
7Kilometers

機械図面では、たとえば、

$INSUNITS = 4

ならmm、

$INSUNITS = 1

ならinch、

$INSUNITS = 6

ならmとして扱う意図を表せます。

ただし重要なのは、

$INSUNITSを書き換えただけで既存座標値が自動的に換算されるわけではない

という点です。

単位を設定することと形状を変換することは別

ここはDXFで特に誤解しやすいところです。

長さ100の線があるとします。

現在、

100 drawing units

で、それをmmとして扱っていたとします。

この図面の$INSUNITSだけをinchへ変更しても、

100

という座標値そのものは100のままです。

つまり、

単位メタデータの変更

と、

幾何座標の尺度変換

は別処理です。

100 mmをinchへ実寸を保ったまま変換するなら、1 inch=25.4 mm1\ \mathrm{inch}=25.4\ \mathrm{mm}

なので、100 mm=10025.4 inch100\ \mathrm{mm} = \frac{100}{25.4}\ \mathrm{inch}≈3.9370 inch\approx3.9370\ \mathrm{inch}

となります。

したがって座標値そのものを、

100

から約、

3.937

へ変換する必要があります。

Autodeskも作図単位そのものを変更・変換する手順として-DWGUNITSなどを案内しており、単なる表示設定とは区別しています。

ezdxfで図面単位を設定する

Pythonのezdxf公式ドキュメントでは、Drawing.unitsを利用できます。

mm図面なら次のように設定できます。

import ezdxf
from ezdxf import units

doc = ezdxf.new("R2010")
doc.units = units.MM

msp = doc.modelspace()
msp.add_line(
    (0, 0),
    (100, 0),
)

doc.saveas("sample-mm.dxf")

このとき、

doc.units = units.MM

は実質的に$INSUNITSへmmを設定する操作です。ezdxfではmmは4に対応します。

HEADERを直接操作することもできます。

doc.header["$INSUNITS"] = 4

ただしコードの意味を明確にするなら、

doc.units = units.MM

のように列挙値を使う方が読みやすくなります。

既存DXFの単位を確認する

既存ファイルなら、

import ezdxf

doc = ezdxf.readfile("drawing.dxf")

print(doc.units)
print(doc.header.get("$INSUNITS"))

のように確認できます。

さらに単位名まで表示したい場合は、

from ezdxf import units

print(units.unit_name(doc.units))

とできます。

この確認はDXFを自動処理する前段に入れておく価値があります。

たとえば、

DXF読込
↓
単位確認
↓
許容単位か判定
↓
必要なら尺度変換
↓
形状解析

という処理フローです。

図形解析を先に行うより安全です。

$INSUNITS=0には注意する

$INSUNITS=0はUnitless、つまり単位未指定です。Autodeskでも値0はUnspecifiedとして定義されています。

この場合、

長さ = 100

という情報だけから、

100 mm

と断定してはいけません。

実務では作図規約や既知寸法などから推定できることもありますが、それはDXF自体から確定した事実とは別です。

自動処理なら、たとえば次のようなルールが安全です。

from ezdxf import units


def require_known_units(doc):
    if doc.units == units.UNITLESS:
        raise ValueError(
            "DXFの単位が未指定です"
        )

「機械図面だからmmだろう」と暗黙に決めるより、未指定状態を検出できる方が堅牢です。

単位変換係数を計算する

ezdxfには、

units.conversion_factor()

があります。

たとえばmmからinchへ変換する係数は、

from ezdxf import units

factor = units.conversion_factor(
    units.MM,
    units.IN,
)

print(factor)

のように求められます。

考え方としては、Ltarget=Lsource×kL_{\mathrm{target}} = L_{\mathrm{source}} \times k

です。

ここで、

  • LsourceL_{\mathrm{source}}:変換前の数値
  • LtargetL_{\mathrm{target}}:変換後の数値
  • kk:単位変換係数

です。

100 mmをinchへ変換するなら、k=125.4k=\frac{1}{25.4}

なので、100×125.4≈3.937100\times\frac{1}{25.4} \approx3.937

となります。

BLOCKには独立した単位を持たせられる

DXF単位をさらに難しくするのがBLOCKです。

ezdxfのドキュメントでは、各BLOCK定義はdocument/modelspaceとは独立したunits属性を持てると説明されています。さらに、異なる単位を持つBLOCKをINSERTするとき、ezdxf自身が暗黙に単位変換を適用するわけではないため、INSERTの尺度として明示的に反映する必要があります。

たとえば、

modelspace = m
block = cm

なら、

1 cm = 0.01 m

なので、挿入尺度を適切に設定する必要があります。

ezdxfでは変換係数を利用して、

factor = units.conversion_factor(
    doc.units,
    block.units,
)

block_ref.set_scale(1.0 / factor)

のように処理できます。

つまりBLOCK/INSERTを自動生成するときは、

挿入位置
回転角
X尺度
Y尺度
Z尺度
単位

を一体として考える必要があります。

$MEASUREMENTとは別物

DXFには、

$MEASUREMENT

というHEADER変数もあります。

名前だけを見ると図面単位そのものに見えますが、$INSUNITSとは役割が違います。

ezdxfの資料では、$MEASUREMENTはmetricまたはimperialのハッチパターンや線種ファイルの選択に関係し、$INSUNITSとは独立していると説明されています。

値は基本的に、

0 = English
1 = Metric

です。

したがって、

$MEASUREMENT = 1

だからといって、

$INSUNITS = 4

つまりmmであるとは限りません。

metric系かどうかと、具体的にmm・cm・mのどれなのかは別問題です。

$LUNITSも長さそのものの単位ではない

さらに、

$LUNITS

もあります。

これも名前が紛らわしい変数です。

$LUNITSは主に線形値の表示形式を指定します。

たとえばDecimal、Engineering、Architectural、Fractionalなどです。ezdxfは、この値はDXF内の実際の長さ値を変換するものではないと説明しています。

したがって、

$INSUNITS
$MEASUREMENT
$LUNITS

は別々に考えます。

整理すると、

$INSUNITS
→ 図面の単位コンテキスト

$MEASUREMENT
→ metric / imperial系の設定

$LUNITS
→ 長さの表示形式

です。

単位をデータ構造として持つ

DXF自動生成では、座標だけをデータとして持つより、単位も明示した方が安全です。

たとえば、

{
  "geometry": {
    "type": "plate",
    "width": 200.0,
    "height": 100.0
  },
  "unit": "mm"
}

とします。

これなら、

200

という裸の数値ではなく、

200 mm

という意味を保持できます。

内部データからDXFを生成するときに、

UNIT_MAP = {
    "mm": units.MM,
    "cm": units.CM,
    "m": units.M,
    "in": units.IN,
}

のように対応付ければ、

doc.units = UNIT_MAP[data["unit"]]

としてDXFへ反映できます。

数値と単位を分離する

さらに再利用性を高めるなら、

{
  "value": 200.0,
  "unit": "mm"
}

という形式も有効です。

たとえば設計データを、

{
  "width": {
    "value": 200.0,
    "unit": "mm"
  },
  "height": {
    "value": 100.0,
    "unit": "mm"
  },
  "hole_diameter": {
    "value": 25.0,
    "unit": "mm"
  }
}

とすれば、DXFだけでなく、

  • CAD生成
  • 設計計算
  • CSV出力
  • Web計算ツール
  • API
  • DB
  • 単位変換

へ同じ設計値を使いやすくなります。

DXF生成時の単位ルールを固定する

自動生成システムでは、プロジェクト内部の基準単位を1つ決める方法が有効です。

たとえば機械設計用途なら、

内部基準単位 = mm

とします。

すると、

入力値
↓
mmへ正規化
↓
計算
↓
CAD座標生成
↓
DXF $INSUNITS = mm

という一貫した処理にできます。

異なる単位を途中で混在させないためです。

ezdxfの公式ドキュメントも、同一プロジェクトで異なる測定単位を混在させず、一つの単位へ統一することを推奨しています。

単位チェックを自動化する

DXFを大量処理するなら、単位は検査項目にできます。

from ezdxf import units


ALLOWED_UNITS = {
    units.MM,
    units.IN,
}


def validate_dxf_units(doc):
    if doc.units == units.UNITLESS:
        return {
            "ok": False,
            "reason": "unitless",
        }

    if doc.units not in ALLOWED_UNITS:
        return {
            "ok": False,
            "reason": "unsupported_unit",
        }

    return {
        "ok": True,
        "reason": None,
    }

さらに、

図面単位
想定外形寸法
既知穴径
タイトルブロック情報

などを組み合わせれば、単位異常をより強く検出できます。

たとえば本来500 mm程度の部品なのにbounding boxが、

0.5 × 0.2

ならm単位の可能性があります。

逆に、

19685 × 7874

のような値ならinch/mm変換の取り違えなどを疑えます。

ただし、この種の判定は推定です。

$INSUNITSのような明示情報と、外形寸法からの推定値は分けて保持するべきです。

よくある誤解

$INSUNITSを変えれば図形サイズも変わる

必ずしもそうではありません。

$INSUNITSの設定と、既存座標値の尺度変換は別の処理として考えます。

$MEASUREMENT=1ならmmである

違います。

metric系であることと、mmであることは同義ではありません。

DXFの100は100 mmである

単位情報を確認しない限り断定できません。

ezdxfへmmを設定すれば既存座標が自動変換される

doc.unitsの設定は単位コンテキストを設定するもので、既存のすべての幾何値を自動的に別単位へ換算する処理とは区別する必要があります。

DXF自動化では単位を最初に扱う

DXF解析ではLINEやCIRCLEなどのエンティティを読むことに目が向きがちです。

しかし自動化を考えるなら、

ファイル
↓
バージョン
↓
単位
↓
座標系
↓
エンティティ
↓
属性
↓
設計情報

という順序で扱う方が安全です。

単位が間違っていれば、その後に正確な図形解析をしても実寸としての意味が崩れるからです。

まとめ

DXFの単位を扱うときに最も重要なのは、

座標値と単位情報を別々に考えること

です。

DXF内の、

100.0

という値は、まず100 drawing unitsです。

図面の単位コンテキストを示す中心的なHEADER変数が、

$INSUNITS

です。

代表値は、

0 = Unitless
1 = Inches
4 = Millimeters
6 = Meters

です。

ezdxfなら、

doc.units = units.MM

で単位を設定でき、

units.conversion_factor()

を使って単位間の変換係数も求められます。

DXFをプログラムで生成・解析するなら、

数値
+
単位
+
変換ルール

をセットでデータ化しておくことが重要です。

そうすれば単なるDXF生成から、

設計データ
↓
単位正規化
↓
計算
↓
CAD形状生成
↓
DXF出力
↓
単位検証

という再利用可能なCAD自動化パイプラインへ発展させられます。

参考になったらシェアしてください
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

機械設計・油圧・CAD・Python・AIなど、ものづくりに関わる技術を扱っています。工学知識を整理・構造化し、設計や自動化に再利用できる形へ変えていくことを目指しています。

目次