図面データベース

図形の変更

AutoLISP関数
(entmod elist)
elist:連想リスト
図形のデータを更新します。
戻り値:連想リスト、またはnil

entmod 関数は、与えられた連想リストの内容にしたがって、図面データベース内の既存の図形を変更します。

elist 引数には、変更を加えた連想リストを指定します。既存の図形を変更するためには与える連想リストに目的の図形の【図形名】が含まれている必要があります。

戻り値は、与えた連想リストがそのまま返ります。変更に失敗した場合は、nil が返ります。

entget 関数のところで作った WRITETEXT コマンドと逆のことを行うコマンドを作ってみます。先ほどのproverv.csv をエクセルなどで新しく編集して provervj.csv を作成しました。これから、ハンドルを頼りに AutoCAD の中の既存の文字を置き換えます。

autolisp entmod sample command

サポート関数として CSV 形式の一行の文字列をリストに変換する csvlineToList 関数を定義します。

(defun csvlineToList (csv / result l i)
  (setq	result nil
	csv    (vl-string-trim " \e\n\r\t," csv)
	l      (strlen csv)
  )
  (while (< 0 l)
    (if	(/= (ascii csv) (ascii "\""))
      (progn				; "で囲まれていない
	(setq i (vl-string-position (ascii ",") csv))
	(if i
	  (setq result (append result (list (substr csv 1 i))))
	  (setq result (append result (list (substr csv 1))))
	)
      )
      (progn				; "で囲まれている
	(setq i (vl-string-position (ascii "\"") csv 1))
	(if i
	  (setq	result (append result (list (substr csv 2 (1- i))))
		i      (1+ i)
	  )
	  (setq result (append result (list (substr csv 2))))
	)
      )
    )
    (setq
      csv (if i
	    (vl-string-left-trim " \e\n\r\t," (substr csv (1+ i)))
	    ""
	  )
      l	  (strlen csv)
    )
  )
  result
)

csvlineToList 関数の使用例は以下の通りです。

_$ (csvlineToList "ハンドル,テキスト")⏎
("ハンドル" "テキスト")
_$ (csvlineToList "100,TEXT String")⏎
("100" "TEXT String")
_$ (csvlineToList "100,\"TEXT,String\"")⏎
("100" "TEXT,String")
_$ (csvlineToList "ONE,TWO,THREE")⏎
("ONE" "TWO" "THREE")

新しく作るコマンドは REPLACETEXT という名前とします。

(defun recursive:Handle+Text->replace (slist / line ename text)
  (if slist
    (progn (setq line  (csvlineToList (car slist))
                 ename (handent (car line))
           )
           (if ename
             (entmod (list (cons -1 ename) (cons 1 (cadr line))))
           )
           (recursive:Handle+Text->replace (cdr slist))
    )
  )
)

(defun read-list (fp / temp result *error*)
  (setq *error* (compose (function (lambda (msg) (close fp))) '*error*))
  (while (setq temp (read-line fp)) (setq result (append result (list temp))))
  result
)

(defun c:replaceText (/ fileName fp)
  (if (and (setq fileName (getfiled "ハンドルとテキストを入力"
                                    (getvar "MYDOCUMENTSPREFIX")
                                    "csv"
                                    (+ 4 16 128)
                          )
           )
           (setq fp (open fileName "r"))
      )
    (progn (recursive:Handle+Text->replace (read-list fp)) (close fp))
  )
  (princ)
)

recursive:Handle+Text->replace 関数は、再帰的に、ハンドルとカンマで区切られたテキストの文字列を csvlineToList 関数で分離し、ハンドルから【図形名】が得られることを確認して、図形のテキストを entmod 関数で変更します。ここでは、entmod 関数に渡す連想リストは、【図形名】とテキストのみを含む最小限のセットです。このように図面データベースは、必要なところのみで融通が利くところがあります。

read-list 関数は、テキストファイルを読み取り、一行ごとに分けられた文字列からなるリストを返します。エラーが起こった際にファイルを閉じるローカルなエラー関数を定義しています。

c:replaceText 関数がコマンドの本体です。

このコマンドを provervj.csv で使用すると、先ほどの図面内の文字が置き換わりました。

autolisp entmod sample command

複合図形の画面更新

AutoLISP関数
(entupd ename)
ename:【図形名】
従属図形を含む複合図形の画面表示を更新します。
戻り値:【図形名】、またはnil

従属図形の図形データは、他のそうでない図形と変更の仕方は同じです。ただし、従属図形の場合、データの更新が画面に直ちに反映されないため、プログラムから更新してやる必要があります。

entupd 関数は、複合図形の画面表示を更新します。

ename 引数は、更新する複合図形の主図形、または従属図形のいずれかの【図形名】を指定します。

戻り値は、指定された【図形名】をそのまま返します。なんらかのエラーが発生した場合は、 nil を返します。

entupd 関数に、複合図形以外のものを与えることは可能ですが、これといって効能はありません。

図形の作成

AutoLISP関数
(entmake [elist])
elist:連想リスト
図面内に新しい図形を作成します。
戻り値:連想リスト、またはnil

entmake 関数は、新規の図形を図面データベースに登録します。図面データベースに作成された図形は、作図ウィンドウに表示され AutoCADユーザーから他と変わりなく編集ができる対象となります。

elist 引数には、作成する図形を表す連想リストを指定します。entmake 関数が一度に処理できるのは一つの図形のみです。省略された場合は、何も起こりません。

連想リストは、図面データベースから得られるものを模して作成すればよいものですが、【図形名】やハンドルはAutoCADが指定するものですから含めません。画層や色、線種といった値が含まれない場合は、現在のAutoCADの設定で補足されます。このように、すべての値について設定されていなくともAutoCAD が自動で値を補い規定値としてくれる場合がありますが、どの値を省略できるのかは確認する必要があります。また、連想リストのデータの順番は、極力、図面データベースから得られるものと同じにしておいた方がエラーを避けることができます。

関数の戻り値は、elist引数で指定した連想リストがそのまま返ります。elist引数が省略されたり、何らかのエラーで図形が作成されなかったりした場合は nil が返ります。

頂点のリストから、線分と 3D ポリラインを作成する関数を作成してみます。AutoCAD でそれぞれの図形を作図してみて、VisualLISPの【コンソール】から次のように実行して、図面データベースから参考にする連想リストを得ます。3D ポリラインの場合は従属図形の頂点のデータを得るために、entsel 関数の代わりに nentsel 関数を使います。

_$ (entget (car (entsel)))⏎        ; 線分を選択
((-1 . <図形名: 7ffff706c50>) (0 . "LINE") (330 . <図形名: 7ffff70 … (後略)
_$ (entget (car (entsel)))⏎        ; 3Dポリラインを選択
((-1 . <図形名: 7ffff706c60>) (0 . "POLYLINE") (330 . <図形名: 7ffff70 … (後略)
_$ (entget (car (nentselp)))⏎      ; 3Dポリラインを選択
((-1 . <図形名: 7ffff706c90>) (0 . "VERTEX") (330 . <図形名: 7ffff70 … (後略)

得られた連想リストの不要な部分を削除して、図形のテンプレートとなる連想リストを定義します。テンプレートの中身は目的に応じて変わりますが、すべての属性について記述されていなくても、付属的なものは AutoCAD により自動的に規定値がセットされます。

(setq *LineTemplate*
       '((0 . "LINE")
         (100 . "AcDbEntity")
         (100 . "AcDbLine")
         (10 0.0 0.0 0.0)               ; start point (WCS)
         (11 0.0 0.0 0.0)               ; end point (WCS)
        )
      *PolylineTemplate*
       '((0 . "POLYLINE")
         (100 . "AcDbEntity")
         (100 . "AcDb3dPolyline")
         (70 . 8)                       ; 1 : closed , 8 : 3D Polyline
        )
      *VertexTemplate*
       '((0 . "VERTEX")
         (100 . "AcDbEntity")
         (100 . "AcDbVertex")
         (100 . "AcDb3dPolylineVertex")
         (10 0.0 0.0 0.0)               ; 3D coordinate (WCS)
         (70 . 32)                      ; 3D Polyline vertex
        )
      *SEQEND* '((0 . "SEQEND"))        ; VERTEX end marker
)

図形の種類によってDXF グループコード 100 のサブクラスマーカーは、省略してよい場合と省略できない場合があるようなので、残しておいた方が安全です。

線分と 3D ポリラインの座標は WCS によりますので、UCS の座標を WCS に変換する関数を用意します。

(defun transform:UCS->WCS (point)
  (trans point acUCS acWorld)
)

頂点のリストを、線分を構成する始点と終点の組に構成しなおす関数は次の通りです。

(defun PointList->LineSegments:sub (plist firstPoint /)
  (cond ((<= 2 (length plist))
         (append (list (list (car plist) (cadr plist)))
                 (PointList->LineSegments:sub (cdr plist) firstPoint)
         )
        )
        ((and (= 1 (length plist)) firstPoint) (list (list (car plist) firstPoint)))
        (T nil)
  )
)

(defun PointList->LineSegments (plist closed /)
  (if (< 1 (length plist))
    (if closed
      (PointList->LineSegments:sub plist (car plist))
      (PointList->LineSegments:sub plist nil)
    )
  )
)

図形を表す連想リストを複数含んだリストを得て、一括して図面データベースに登録する関数を用意します。戻り値は、最後に登録した図形の【図形名】を返します。

(defun entmake-list (entityDataList / entityData)
  (foreach entityData entityDataList (entmake entityData))
  (entlast)
)

さて、頂点リストから複数の線分を描く関数は次のようになります。LineEntityData 関数は一本分の線分の連想リストを生成します。drawLines 関数がここでの本体で、引数の頂点リストは UCS に基づくものとして WCS に変換して使用しています。

(defun LineEntityData (lineSegment / temp)
  (setq temp (subst (cons 10 (car lineSegment)) (assoc 10 *LineTemplate*) *LineTemplate*))
  (subst (cons 11 (cadr lineSegment)) (assoc 11 temp) temp)
)

(defun drawLines (plist closed /)       ; plist is UCS
  (entmake-list
    (mapcar 'LineEntityData
            (PointList->LineSegments (mapcar 'transform:UCS->WCS plist) closed)
    )
  )
)

3D ポリラインを描く関数は次の通りです。3D ポリライン は複合図形であり、主図形のデータの後に従属図形である頂点のデータが並び、SEQEND で終了します。3DPolylineData:vertexs 関数は、再帰的に頂点部分の連想リストを作成します。3DPolylineData 関数は、主図形のデータ、従属図形のデータ、SEQEND をまとめます。最後の draw3DPolyline 関数がここでの本体です。引数の頂点リストは UCS に基づくものとして WCS に変換して使用しています。

(defun 3DPolylineData:vertexs (plist /)
  (if plist
    (append
      (list (subst (cons 10 (car plist)) (assoc 10 *VertexTemplate*) *VertexTemplate*))
      (3DPolylineData:vertexs (cdr plist))
    )
  )
)

(defun 3DPolylineData (plist closed / temp)
  (append (list (progn (setq temp (assoc 70 *PolylineTemplate*))
                       (subst (cons 70
                                    (if closed
                                      (logior (cdr temp) 1)
                                      (logand (cdr temp) (~ 1))
                                    )
                              )
                              temp
                              *PolylineTemplate*
                       )
                )
          )
          (3DPolylineData:vertexs plist)
          (list *SEQEND*)
  )
)

(defun draw3DPolyline (plist closed /)       ; plist is UCS
  (entmake-list (3DPolylineData (mapcar 'transform:UCS->WCS plist) closed))
)

drawLines 関数と draw3DPolyline 関数の使用例は以下の通りです。図形が作成され、作図ウィンドウに表示されます。

_$ (drawLines '((0 0) (100 0) (100 100) (0 100)) T)⏎
<図形名: 7ffff707040>
_$ (draw3DPolyline '((0 0) (100 0) (100 100) (0 100)) nil)⏎
<図形名: 7ffff707050>

図形データベースに直接アクセスした場合は、当然ながら【コマンドライン】にコマンドのエコーはありません。システム変数 BLIPMODE によるマーカーも出ませんし、オブジェクトスナップが影響することもありません。したがって、システム変数を変えておく必要はありませんが、UNDO コマンドで図面変更をグループ化しておいた方が使い勝手がよくなります。ちなみに、何も対処しないで ユーザーが UNDO を行うと、図形データベースに直接アクセスした変更はもとより、その前のコマンドまで UNDO の対象になります。

図形の削除

AutoLISP関数
(entdel ename)
ename:【図形名】
図形を削除、または削除した図形を復元します。
戻り値:【図形名】

entdel 関数は、図形を削除します。

ename 引数は、削除する図形の【図形名】を指定します。ただし、ブロック定義の中の図形や複合図形の従属図形は削除できません。これらは、AutoCAD のコマンドを使用する他、ActiveX によるアクセスで直接削除することができます。

戻り値は、削除した【図形名】です。

特徴としては、もう一度同じ【図形名】を entdel 関数にかけると図形が削除前の状態に戻り、画面にも復活します。なお、ActiveX の VLAオブジェクトの Delete メソッドを使用して削除した場合は、このような復活はありません。