コラム

  • HOME
  • コラム
  • AutoLISPで業務を効率化する 第三回【図形の操作】

AutoLISPで業務を効率化する 第三回【図形の操作】

AutoLISPで業務を効率化する 第三回【図形の操作】

第二回は、AutoLISPの初歩的な部分の内、VisualLISPの立ち上げやコマンドの作成方法等について以下の記事で解説しました。

AutoLISPで業務を効率化する 第二回【開発を始める】

AutoLISP開発の基本の締めとして、第三回は図形名や選択セット等の図面内のオブジェクトを操作する方法について簡単に解説していきます。

これらが理解できれば、後は関数の組み合わせである程度のことは可能になります。

 

サンプルコードで解説

まずは新しいファイルを開きましょう。(新しいコマンドを試す時は破棄してもよいファイルで試しましょう)

適当な場所に円をつくってから、以下のコマンドを実行し、クリックしてみてください。

(defun c:TEST01 ( / ss en dxf)
(setq 
ss (ssget "_:S+.")
en (ssname ss 0)
dxf (entget en)
);setq
(if (assoc 10 dxf)
(entmod (subst (cons 10 '(0 0 0)) (assoc 10 dxf) dxf))
(entmod (append dxf (list (cons 10 '(0 0 0)))))
);if
(princ)
);defun

円が原点に移動したと思います。

 

このコマンドは、図形の定義データを編集し、中心座標を原点に変えます。

 

フローチャート

このスクリプトのフローチャートです。

フローチャートを使うと、構造を整理し、全体像を把握することができます。LISPを作成する際は、一旦このような図でまとめてみると良いでしょう。今回は簡易的なものなのであまり問題にはなりませんが、処理によっては1万行を超える場合もあります。

また、簡単な処理のように思えて、実際に記述すると想像以上に複雑だったり、処理の方向性を間違えて1からやり直しになったりすることもあるので、基本的にはこういった図から考え始めるのが良いと思います。

 

解説

要所をかいつまんで説明します。今回は関数の詳細な説明は省くので、気になった方は適宜、Autodesk公式の「関数リファレンス」を確認してください。

まずは、ssget関数でユーザーに図形を選択させて、「選択セット」を取得します。

 (ssget "_:S+.")

選択セットは文字通り選択された図形の「図形名」を格納したセットです。選択セットの中の図形の順番は、0から始まる「インデックス番号」で管理されており、ssname関数はインデックス番号を与えることで、任意の順番の図形名を取り出すことができます。

(ssname (ssget "_:S+.") 0)

次に、図形名の入った変数enをentget関数に渡し、「図形定義データ」を取り出します。

単体でこの処理のみを実行するとわかりますが、図形定義データは括弧の中に複数のデータが入ったものです。この構造を「リスト」と呼びます。

(entget (ssname (ssget "_:S+.") 0))

図形定義データのリストは、更に複数の「ドットペア」と呼ばれる二組の要素からなるリストが格納されており、この「左側の整数」が始点の座標や色、図形の種類等を定義、右側の要素がその具体的な値を表します。そして、この左側の整数のことを「DXFグループコード」と呼びます。

今回は少し特殊な形で記述されていますが、assoc関数を使うと、このDXFグループコードを用いて図形定義データから図形のプロパティを取得することが可能です。

(assoc 10 (entget (ssname (ssget "_:S+.") 0)))

「選択セット」→「図形名」→「図形定義データ」→「DXFグループコード」→「プロパティ」という流れは、LISPで図形を操作する上では非常に良く用いるので、覚えておいてください。

 

末尾の(princ)について

念のため、最後の(princ)について説明しておきます。defun関数は関数を定義します。引数にc:を仕込むことでコマンドになりますが、根本は関数です。必要なのは中身の処理そのものですが、関数であるため、戻り値が発生してしまいます。

最後の処理が戻り値となるため、TEST01の戻り値は図形定義データです。 (princ)を置いておくと、最後の戻り値が空になってすっきりします。LISPのユーザーは開発者だけとは限らないため、混乱を避けるためにも置いておくと良いでしょう。

 

個別に解説

先ほどの「括弧」付きの要素を詳しく解説していきます。

 

図形名

AutoCADのオブジェクトには「図形名(entitiy name)」が割り振られています。

図形名は重複することがありません。形状や、座標、長さ等のプロパティが全く同値であっても、図面内に複数存在している以上はそれぞれ別の図形名が割り振られます。

ブロックも同様です。

LISPで図形を操作する時は、基本的にこの図形名を中心に扱う事になります。

Autodesk公式では「概要 – 図形名(AutoLISP)」に記載があります。

 

図形名のルール

図形に変更を加えた場合はどうなるでしょうか。これは、新たな図形ができているかどうかで処理が異なります。

延長や、尺度変更コマンドを使っても図形名はそのままです。これはプロパティが変更されているだけで、新たに図形ができているわけではないからです。

対して、複写をした場合は新たに図形ができているため、新たに図形名が割り振られます。この場合、コピー元の図形名は変わりません。

鏡像や回転コマンドのコピーは言わずもがな、点で部分切断(BREAKATPOINT)やトリムなどで図形が複数に別れた時も同様に、元の図形名は据え置きで新しい図形名が生じます。

新しい図形名が二つ割り振られるわけではありません。後者の場合、元々の始点に近い方が元の図形名となります。

ただし、ポリラインを分解した場合は、生じた全ての図形に対して、元の図形名とは別の新しい図形名が割り振られます。これはポリライン自体のプロパティが編集された結果図形が生じたわけではなく、新たに線分という図形が生じているからです。

操作の例

図形名の処理

延長、尺度変更

変更なし

複写、点で部分切断

元の図形名+新たな図形名

分解

新たな図形名

 

図形名は一度図面を閉じると変わる

余談ですが、図形名は一度図面を閉じると消失し、図面を開いた際に新たに割り振られます。実は、AutoCAD自体はC++という言語で構築されており、LISPが利用できるのはあくまでこのC++のブラックボックスの中からあえて名前を付けられている部分だけです。

C++では、メモリ内のオブジェクトをポインタというアドレスを指し示す変数で参照することにより処理を行っているため、ポインタの1つである図形名は、図面を閉じてメモリが解放されるたびに消えてしまうというわけです。

また、私たちがLISPを使って目にする図形名とは、C++のデータをLISPで利用するために変換し、人が理解できるように加工された表示形式にすぎません。つまりこの文字列自体は何の意味も持たないので、(entget <図形名: ~>)のように文字列を入力してもエラーが返るだけなので気を付けてください。

 

インデックス番号

インデックス番号(index)」は0から始まる整数です。選択セットや、リスト等のデータ構造の中身はこのインデックス番号が割り振られています。データ構造内のデータが入っている部屋番号みたいなものだと思ってください。

ssname関数やnth関数など多くの関数で、このインデックス番号を利用してデータ構造から要素を取り出す等の操作を行います。

 

選択セット

AutoCADでオブジェクトを選択すると、この「選択セット(Selection set)」が生じます。図形名と同様に、図面を開いてからは重複することはありませんし、閉じると消えます。

つまり選択セットもポインタの1つということです。また、サンプルコードを見るとわかりますが、何かを選択すれば図形が1つでも選択セットが生じます。

Autodesk公式では、「概要 – オブジェクトの選択と選択セット(AutoLISP)」に記載があります。

 

選択セットのルール

図形を選択すると、新しい順に図形名が整列された状態で選択セットに格納されます。

つまり、外見上の図形の順番と、実際に処理される順番が異なる事があります。

選択セットは選択のたびに生じるため、普段AutoCADで作業しているときも、裏では選択セットが生じていることになります。

また、ssget関数は選択セットを作成する関数ですが、この関数を使う際に複数回に分けて選択した場合は、そこで生じた複数の選択セットを作成順に結合して選択セットにします。

つまり、選択した図形を外見上の並び順で処理したい場合は、選択セットをそのまま使うのではなく、リスト等で図形名の順序をソートしてから処理する必要があるので気を付けましょう。

 

リストとドットペア

 

リスト(list)」とは何かを詳しく語ると記事が丸々一つできてしまったので、ここでは簡単に「複数の要素を入れられる箱のようなもの」と説明しておきます。

変数が1つしか要素が入らないのに対して、リストは複数の要素を格納できます。また、選択セットは図形名しか入りませんが、リストは好きな要素を入れることができます。非常によく利用するので、興味があれば公式の「概要 – リスト(AutoLISP)」を確認しておいてください。

ドットペア(Dotted Pairs)」は、リストの要素をドットで区切って表示したものです。AutoCADでは要素が2つしかないリストはドットペアで表示されます。こちら「概要 – ドット ペア(AutoLISP)」に記載がありますが、これだけでは理解が難しいと思うので次回以降詳しく解説する予定です。

 

図形定義データとDXFグループコード

図形定義データ(Definition Data)」はリストの中に複数のドットペアが格納されています。このような形式のリストのことを連想リストと言い、ドットペアは(キー . データ)として扱う事ができます。

つまり「DXFグループコード」はこのキーであり、厳密に言うとassoc関数はキーを使って連想リストからリストを取り出しているわけですね。

DXFグループコードとプロパティの対応についてはAutoCAD公式の「DXF グループ コード(番号順)リファレンス」に記載されております。関数リファレンス同様に、活用しましょう。

図形定義データの操作関数については「概要 – 図形データ関数(AutoLISP)」に記載があります。

 

ハンドル

ハンドル(handle)」とは図形名と同様に、重複することなく図形に割り振られるラベルです。図形名と大きく異なる点として、ハンドルは図面を閉じても消えません。つまりハンドルはポインタではなく、dwgファイルの中に保存されるデータの1つです。

あえて使う事は少ないですが、図形名と違いハンドルを表す文字列は意味を持つので、エラーで図形が消えた際には復元に使用することができます。

公式はこちら「概要 – 図形ハンドルとその使用(AutoLISP)

 

四則演算・論理演算

サンプルコードには登場しませんでしたが、LISPで四則演算を行う方法にも触れておきます。足し算「+」引き算「-」掛け算「*」割り算「/」というのはExcelを代表的に広く使われている通りですが、LISPでは(関数 引数)という形をとるため、例えば1+2を記述する場合は(+ 1 2)とする必要があります。

算術演算関数リファレンス(AutoLISP)」に網羅されているので確認すると良いでしょう。

また、AND(論理積)やOR(論理和)、=(イコール)等の比較演算も同様に関数として記述します。こちらも、「比較演算関数と条件関数リファレンス(AutoLISP)」に網羅されています。

 

まとめ

今回は、図形の操作について解説しました。AutoCADの主目的は作図、つまりは図形を作成することなので、本記事の内容でさまざまなことが可能になると思います。

とはいえ、三回に渡ってLISP開発の初歩を解説してきましたが、あくまでこれは氷山の一角に他なりません。まだまだ、解説できることはあるため、次回以降はより一層深い内容も解説していこうと思います。

最後に、用語とテストコードを記載して、締めとさせていただきます。

 

用語まとめ

名前

内容

図形名

図面内のオブジェクトのラベル

<図形名: 20ed2085530>

選択セット

オブジェクトを選択した際のグループ

<Selection set: 38b1>

リスト

複数の要素が括弧で括られたデータ構造

(A B (C D))

ドットペア

2つの要素からなるリストをドットで区切った表記

(A . B)

図形定義データ

図形のプロパティが格納されたリスト

((-1 . <図形名: 20ed2085530>) (0 . “CIRCLE”) …)

DXFグループコード

図形定義データ内でプロパティの種類を表す整数

0:種類、1:入力された文字列、2:ブロック名・属性名…etc

ハンドル

オブジェクトのラベル(閉じても消えない)

“9E33”

インデックス番号

データ構造を参照する際の識別番号

インデックス番号3:(nth 3 (A B (C D)))→(C D)

 

テストコード

動画で使用したコードを記載しておきます。使い道がわからないLISPですが、何かの参考になればと思います。

【TESE02】クリックした所にテキストを配置し、x座標の小さい順に0から数値をつける。

(defun c:TEST02 (/ pt en-new lst-en lst-srt i en dxf)
(vl-load-com);拡張機能のロード(お守りみたいなもの)
;
(setq lst-en '()) ; 図形名を格納するリスト
(princ "\n■クリックするたびに、左から順に0, 1, 2...と数値を更新")
;
;
; クリックを続ける限りループ
(while (setq pt (getpoint "\n配置場所をクリック (終了は右クリック/Enter): "))

; 1. 新しいテキストを配置(空文字)
(entmake 
(list
(cons 0 "TEXT")
(cons 10 pt)
(cons 40 (getvar "TEXTSIZE"))
(cons 1 "") ; 最初は空文字
);list
);entmake
(setq 
en-new (entlast); 作成した図形名を取得
lst-en (cons en-new lst-en); リストに追加
);setq 

; 2. ソート用のリスト作成: ((x座標 . 図形名) (x座標 . 図形名) ...)
(setq lst-srt
(mapcar 
'(lambda (en) (cons (car (cdr (assoc 10 (entget en)))) en)) 
lst-en
);mapcar
);setq

; 3. x座標が小さい順に並び替え
(setq lst-srt (vl-sort lst-srt '(lambda (a b) (< (car a) (car b)))))

; 4. 並び替えたリストの順番通りに、図形の中身(DXF 1番)を書き換える
(setq i 0)
;
(foreach item lst-srt
(setq 
en (cdr item) ; 図形名を取り出す
dxf (entget en) ; 図形データを取得
dxf (subst (cons 1 (itoa i)) (assoc 1 dxf) dxf); 既存の文字列を新しい数値(i)に置換
);setq
;
(entmod dxf); 図形情報を更新
(setq i (1+ i))
);foreach
;
);while
(princ "\n■コマンド終了")
(princ)
);defun

【TESE03】選択したテキストの値を選択セットの順番に0から書き換える。

(defun c:TEST03 (/ ss i en dxf str )
(setq 
ss (ssget '((0 . "TEXT")))
i 0
);setq
; 選択セットの図形の数だけ繰り返す
(repeat (sslength ss)
(setq 
en (ssname ss i)
dxf (entget en)
);setq
(if (assoc 1 dxf)
(entmod (subst (cons 1 (itoa i)) (assoc 1 dxf) dxf))
(entmod (append dxf (list (cons 1 (itoa i)))))
);if
(setq i (1+ i))
);repeat
(princ)
);defun

 

 

 

 

なんでもお気軽に
ご相談ください!

06-6195-3991

国土工営コンサルタンツ(株) 
営業時間:平日9:00 - 17:30

フォームから問合せする