「DXFのGROUPオブジェクトを読む|名前付きグループ・コード340・エンティティ集合」の内容を表す技術イラスト
UNIT 03CAD・3Dプリント FIG.00 · EYECATCH

DXFのGROUPオブジェクトを読む|名前付きグループ・コード340・エンティティ集合

DXFのOBJECTSセクションにあるGROUPを、ACAD_GROUP辞書、名前付き・匿名フラグ、選択可否、コード340のハンドル列から解説します。ブロックとの違い、参照解決、Pythonでの不整合検査まで整理します。

DOC NO.EKP-03-1059
DATE
REVB
READ約 10 分
目次

DXFのGROUPは図形ではなく参照の集合

CAD上で複数の線、円、文字などをまとめて選択したいとき、グループ機能を使うことがあります。見た目は一つのまとまりとして操作できますが、DXFではグループ全体が新しい図形へ変換されるわけではありません。

各LINEやCIRCLEは独立したエンティティのまま残り、OBJECTSセクションのGROUPオブジェクトが、それらのハンドルを参照します。

GROUPオブジェクト
    ├── 340 → LINE
    ├── 340 → CIRCLE
    └── 340 → TEXT

この構造を理解すると、グループ情報の復元、ブロックとの区別、参照切れの検査が可能になります。

GROUPオブジェクトはOBJECTSセクションにある

GROUPは画面へ直接描画されるエンティティではなく、非図形オブジェクトです。そのため、通常はENTITIESではなくOBJECTSセクションへ保存されます。

一方、グループのメンバーとなるLINE、ARC、TEXTなどは、それぞれのモデル空間またはペーパー空間に存在します。

要素保存場所役割
GROUPOBJECTSメンバーの参照と設定を保持
LINE・CIRCLEなどENTITIES実際の形状を保持
ACAD_GROUP辞書OBJECTSグループ名とGROUPを対応付ける

ENTITIESだけを解析すると図形は取得できても、グループ所属は復元できません。

GROUPの主要グループコード

AutodeskのDXF仕様で定義されている主要コードは次のとおりです。

グループコード内容
0オブジェクト型GROUP
5GROUP自身のハンドル
102persistent reactorグループの境界
330所有辞書などへのsoft-pointer参照
100サブクラスマーカーAcDbGroup
300グループの説明
70匿名フラグ。1は匿名、0は名前付き
71選択可否。1は選択可能、0は選択不可
340メンバーエンティティへのhard-pointer参照

メンバー数を表す専用コードはありません。コード340が現れた回数と、解決できた参照先の数を分けて数えます。

GROUPのDXF例を読む

次の例では、ハンドルA1のGROUPが、3個のエンティティを参照しています。

0
GROUP
5
A1
330
2F
100
AcDbGroup
300
Pump skid outline
70
0
71
1
340
10A
340
10B
340
10C

読み取れる内容は次のとおりです。

  • GROUP自身のハンドル:A1
  • 所有辞書のハンドル:2F
  • 説明:Pump skid outline
  • 名前付きグループ:コード70が0
  • 選択可能:コード71が1
  • メンバー:10A、10B、10C

コード340は繰り返し出現します。通常の辞書型へ直接変換すると最後の値で上書きされるため、順序付き配列として保持します。

{
  "handle": "A1",
  "owner_dictionary": "2F",
  "description": "Pump skid outline",
  "unnamed": false,
  "selectable": true,
  "member_handles": ["10A", "10B", "10C"]
}

グループ名はGROUP本体に保存されない

GROUPオブジェクトには、グループ名を直接保存する専用コードがありません。名前は、名前付きオブジェクト辞書からたどるACAD_GROUP辞書のキーとして管理されます。

概念的な参照関係は次のようになります。

名前付きオブジェクト辞書
    └── ACAD_GROUP → グループ辞書
                         └── PUMP_SKID → GROUP A1
                                            ├── 340 → 10A
                                            ├── 340 → 10B
                                            └── 340 → 10C

辞書部分を簡略化すると、次のような名前とハンドルの組になります。

3
PUMP_SKID
350
A1

GROUPだけを単独で読んでも名前PUMP_SKIDは取得できません。辞書キーと参照先GROUPを対応付ける必要があります。

コード70が1の匿名グループにも、実際の辞書管理上は*A...形式の自動生成名が付くことがあります。表示名が不要に見えても、再出力や参照解決のために辞書キーを保持します。

コード340は所有ではなくhard pointer

コード340はhard-pointer handleです。GROUPはメンバーを参照しますが、LINEやCIRCLEの形状データを内包しているわけではありません。

GROUPとメンバーの削除処理は分けます。グループ解除は参照集合を取り除く操作であり、通常は図形自体を消す操作ではありません。

また、DXFのハンドル参照は、図面間コピーやINSERT、XREFなどで変換対象になります。独自のDXF結合処理では、GROUP内の340も新しいハンドルへ更新しなければ、コピー後に旧図面のエンティティを指す参照切れが発生します。

GROUPとBLOCKの違い

GROUPとBLOCKは、データ構造と用途が異なります。

項目GROUPBLOCK
役割既存図形の選択集合再利用する図形定義
保存各レイアウトの図形を参照BLOCKS内に定義
配置元図形を参照INSERTで配置
変換配置変換を持たない尺度・回転を設定可能

同じ形状の反復配置にはBLOCK、既存図形の論理的な集合にはGROUPが適します。両者を自動変換すると、所有場所や編集単位が変わるため注意が必要です。

メンバーは同じレイアウトに存在する

ezdxfでは、1個のグループのメンバーは同じレイアウトに存在し、モデル空間とペーパー空間をまたがず、ブロック定義内の図形も含めないことが条件です。

独自パーサーでは、各メンバーのハンドルを解決した後、次を検査します。

  • すべての参照先が存在するか
  • 参照先が図形エンティティか
  • 全メンバーが同じモデル空間またはペーパー空間に属するか
  • ブロック定義内のエンティティが混ざっていないか
  • 同じハンドルが重複登録されていないか

違反を見つけても自動移動せず、GROUP、辞書名、メンバー、所属レイアウトを診断情報として出力します。

PythonでGROUPを解析する

順序付きタグ列からGROUP固有の値を抽出する例です。

from dataclasses import dataclass


@dataclass
class GroupData:
    handle: str | None
    owner_handle: str | None
    description: str
    unnamed: bool
    selectable: bool
    member_handles: list[str]


def normalize_handle(value: object) -> str:
    return str(value).strip().upper()


def parse_group(
    tags: list[tuple[int, object]],
) -> GroupData:
    handle = None
    owner_handle = None
    description = ""
    unnamed = False
    selectable = True
    members: list[str] = []

    for code, value in tags:
        if code == 5:
            handle = normalize_handle(value)
        elif code == 330:
            owner_handle = normalize_handle(value)
        elif code == 300:
            description = str(value)
        elif code == 70:
            unnamed = int(value) == 1
        elif code == 71:
            selectable = int(value) == 1
        elif code == 340:
            members.append(normalize_handle(value))

    return GroupData(
        handle=handle,
        owner_handle=owner_handle,
        description=description,
        unnamed=unnamed,
        selectable=selectable,
        member_handles=members,
    )

コード330はコード102のreactorグループ内にも現れ得ます。タグ位置と境界を保持し、所有参照とreactor内参照を区別します。

ハンドル索引でメンバーを解決する

図面全体のオブジェクトを読み込んでハンドル索引を作成した後、コード340を解決します。

def resolve_group_members(
    group: GroupData,
    handle_index: dict[str, dict],
) -> tuple[list[dict], list[str]]:
    resolved = []
    missing = []

    for handle in group.member_handles:
        entity = handle_index.get(handle)
        if entity is None:
            missing.append(handle)
        else:
            resolved.append(entity)

    return resolved, missing

解決後は、グループ名、設定、元のハンドル列、解決済みエンティティ、未解決ハンドルを分けて保存します。元の列を残せば、修復や再出力を別工程で判断できます。

実装で起こりやすい失敗

  • ENTITIESだけを読む:GROUPとACAD_GROUP辞書を取得できません。
  • コード340を一つの値として保存する:複数メンバーが上書きされます。
  • コード300をグループ名とみなす:300は説明であり、名前は辞書キーです。
  • GROUPをBLOCKとみなす:再利用定義、挿入点、尺度、回転の構造はありません。
  • 参照先を解決せず文字列のまま使う:所属レイアウトやエンティティ型を検査できません。
  • グループ解除でメンバーを削除する:参照集合と図形のライフサイクルを混同しています。
  • 図面結合時に340を変換しない:コピー後のハンドル対応が崩れます。
  • すべての330を同じ意味で扱う:コード102の文脈やサブクラス位置を確認する必要があります。

まとめ

DXFのGROUPは、複数の図形を一つの図形へ変換する仕組みではありません。OBJECTSセクションのGROUPオブジェクトが、コード340で既存エンティティを参照する構造です。

ACAD_GROUP辞書
    ↓ 名前からGROUPを取得
GROUP
    ↓ コード340を順番に解決
LINE・CIRCLE・TEXTなど

主要項目は、コード300の説明、70の匿名フラグ、71の選択可否、繰り返し現れる340です。グループ名はGROUP本体ではなく辞書側に保存されます。

解析ではハンドル索引を作り、辞書名とGROUP、GROUPとメンバーを二段階で解決します。そのうえで参照切れ、重複、レイアウト混在を検査します。

この構造を保持できれば、CAD上の論理的な選択集合を、部品候補、注記セット、検査対象、変換単位などの設計データとして再利用できます。

参考情報

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

この記事を書いた人

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

目次