Civil3DのJツールコマンドのひとつ「等高線標高設定/入力」をAutoCADで使えるようにAutoLISPで作ってみる。

## Civil3D、Jツールとは
Autodesk社が提供している土木設計用のBIM/CIM対応のCADソフトです。
国交省案件においてBIM/CIM原則対応とのことで、今後使用を求められているソフトです。
ただし、非常に高価格(関連ソフトをまとめたパッケージが50万円以上/年)です。

Jツールはその中でも日本仕様プログラムとして電子納品や数量計算などの
追加コマンドやアセンブリや標準の図面仕様も含まれています。

## 等高線標高設定/入力 コマンドとは
Z座標、高さ情報の含まれていないポリライン、等高線に高さを与えるコマンドです。
提供されるデータは通常はZ座標も含まれた３次元データが一般的ですが
CADの共通様式を経由するなど変換をかけた結果や作業者の都合で高さ情報を失うことはままあります。
BIM/CIMでは３次元データである必要があるため、必然的に高さ付けの作業が求められます。

## AutoCADで使えるAutoLISPでの高さ付けコマンド
前述の通り、Civil3Dは非常に高価格です。
等高線に高さを付けるだけであればAutoCADでも出来ると思い作成しました。

### プログラムの流れ
基本的には、「等高線標高設定/入力」コマンドと同じになるように考えます。
流れとしては以下です。
1.ダイアログボックスを表示、主曲線、計曲線の間隔、変更レイヤーを設定
2.高さを付ける等高線をまたぐように2点クリックで直線を指示
3.基準となる高さを入力
4.高さとレイヤーをそれぞれ変更
5.繰り返し(2～5）

### ダイアログボックスの作成、ダイアログボックスからの取得
今回のダイアログボックスの作成で面倒なのは、
レイヤー名一覧を取得してプルダウンリストにする必要があるところです。

一番簡単なダイアログボックスを表示する手順としては
あらかじめ「.dcl」を作成して、ファイル先を参照する方法です。
しかし、コマンドを実行した時点でのレイヤー名一覧を必要とするため、
AutoLISP内でダイアログボックスの作成をしなければいけません。

#### ダイアログボックスをLISPコマンド内にて作成
順序としては以下の通りです。
1.DCLファイルをテンポラリフォルダに作成
2.DCLファイルに情報書き込み
3.DCLファイルへの初期値のセット、入力値を取得などの処理
4.DCLファイルを削除
~~~lisp
(setq file-open (open (setq file-name (vl-filename-mktemp "" "" ".dcl")) "w"))
  (foreach str
  '(

  ;DCLファイルの内容、文字列リスト

   )
   (write-line str file-open)
  )
(close file-open)


;ダイアログボックスへの処理

(vl-file-delete file-name);DCLファイルの削除

~~~

「DCLファイルの内容、文字列リスト」ですが、
DCLファイル上では下記のように書かれる必要があります。
~~~
   :popup_list { label = "レイヤー名一覧"; 
     key = "layers";
     allow_accept = true;
     multiple_select = false;
    }//list_box  
~~~
注意する点として、
「DCLファイルの内容、文字列リスト」の部分ですが
「"」は、単一だとLISP内の文字列の判別に使われるため「\\"」と書くことが必要です。

実際のAutoLISPファイル上では下記のように書く必要があります。

~~~LISP
"   :popup_list { label = \"レイヤー名一覧\"; "
"     key = \"layers\"; "
"     allow_accept = true; "
"     multiple_select = false;"
"    }//list_box  "
~~~
上記に「ダイアログボックスの名称」「OK/Cansel」を付け加えます。
今回は`setting_chelev`という名称にします。

ここまでをまとめると下記のようになります。
~~~lisp
(setq file-open (open (setq file-name (vl-filename-mktemp "" "" ".dcl")) "w"))
  (foreach str
  '(
    "setting_chelev :dialog"
    "{ label = \"等高線標高設定\";"
    "   :popup_list { label = \"レイヤー名一覧\"; "
    "     key = \"layers\"; "
    "     allow_accept = true; "
    "     multiple_select = false;"
    "    }//list_box  "
    "ok_cancel;"
    "}"
   )
   (write-line str file-open)
  )
(close file-open)
~~~
上記では、テンポラリフォルダに.dclファイルが作成され、
レイヤー名一覧と書かれたプルダウンリストが作成されます。
しかし、プルダウンリストの中身が入力されていないため
空のプルダウンになっています。

#### レイヤー名一覧の取得
プルダウンリストに表示するためには、レイヤー名一覧を現在の図面から取得し、`(start_list)`関数などを用い、プルダウンリストの中身を作成する必要があります。

下記では`layers`にレイヤー名一覧がリストで格納されます。
~~~lisp
;---------------------------------------------
;シンボルテーブルをリストで返す
;---------------------------------------------
;[table-name]:"LAYER","LTYPE","VIEW","STYLE","BLOCK","UCS","APPID","DIMSTYLE","VPORT"
;[return]:table-name list
(defun table-names (table-name / tbl lst)
  (if (setq tbl (tblnext table-name T))
    (progn
      (setq lst (list (cdr (assoc 2 tbl))))
      (while (setq tbl (tblnext table-name))
        (setq lst (append lst (list (cdr (assoc 2 tbl)))))
      )
    )
  )
)

(setq layers (table-names "LAYER"))

~~~

レイヤー名一覧を取得したあと、`setting_chelev`を呼び出し
`"layers"`にレイヤー名一覧を入力します。
~~~lisp
 (new_dialog "setting_chelev" (setq dcl-id (load_dialog file-name)))
 (start_list "layers")
 (mapcar (function add_list) layers)
 (end_list)
~~~

これでプルダウンリストにレイヤー一覧が格納されて表示するようになります。
今度はプルダウンリストで選ばれたレイヤー名の取得の仕方です。

OKボタンが押された場合、レイヤー名を取得します。
他にも取得する値があるため、別に`chelev_getvar`という関数を組みます。
`get_tile`関数で帰ってくる値は、リストから何番目であるかの「文字列」であるため、
`(atoi)`で文字列から数値に変えたのちに、`(nth)`で`Layers`から抜き出さなくてはいけません。
~~~LISP
;OK、キャンセルの処理
 (action_tile "accept" "(chelev_getvar)(done_dialog 1)")
 (action_tile "cancel" "(done_dialog 0)")
 (setq act (start_dialog))
 (unload_dialog dcl-id)

;OKの場合のデータ取得
(defun chelev_getvar ( / )
  (setq layer (nth (atoi (get_tile "layers")) layers))
)
~~~


ここまでを`chelev_setvar`という関数にまとめるとこうなります。

~~~LISP
(defun chelev_setvar (/ file-open file-name str dcl-id layers)
  (defun table-names (table-name / tbl lst)
    (if (setq tbl (tblnext table-name T))
      (progn
        (setq lst (list (cdr (assoc 2 tbl))))
        (while (setq tbl (tblnext table-name))
          (setq lst (append lst (list (cdr (assoc 2 tbl)))))
        )
      )
    )
  )

  (setq layers (table-names "LAYER"))

  (setq file-open (open (setq file-name (vl-filename-mktemp "" "" ".dcl")) "w"))
    (foreach str
    '("setting_chelev :dialog {"
      " label = \"等高線標高設定\";"
      "  :popup_list {"
      "     label = \"レイヤー名一覧\";"
      "     key = \"layers\";"
      "     allow_accept = true;"
      "     multiple_select = false;"
      "  }//popup_list"
      "ok_cancel;"
      "}"
     )
    (write-line str file-open)
    );foreach
  (close file-open)

  (new_dialog "setting_chelev" (setq dcl-id (load_dialog file-name)))
  (start_list "layers")
  (mapcar (function add_list) layers)
  (end_list)

  (action_tile "accept" "(chelev_getvar)(done_dialog 1)")
  (action_tile "cancel" "(done_dialog 0)")
  (setq act (start_dialog))
  (unload_dialog dcl-id)
  (princ)
)

  ;OKの場合のデータ取得
(defun chelev_getvar ( / )
    (setq *layer* (nth (atoi (get_tile "layers")) layers))
)


~~~
`(chelev_setvar)`を実行するとダイアログボックスのプルダウンからレイヤーを選択できます。
その後、グローバル変数である`*layer*`に選択したレイヤー名が格納されます。


### 実際に作ったダイアログ
実際に私が作成したダイアログまわりのコードは長いので折りたたんでおきます。
<details><summary>ダイアログ周りのコード</summary>

~~~LISP


(defun chelev_setvar (/ file-open file-name str dcl-id hicn_layer_lst lwcn_layer_lst)
 (setq file-open (open (setq file-name (vl-filename-mktemp "" "" ".dcl")) "w"))
 (foreach str
    '("// chelev.dcl for use in chelev.lsp  //"
      "setting_chelev :dialog"
      "{ label = \"等高線の設定\";"
      "//--------------------------------------------------------"
      " : row {"
      "  : column {//colmun1_START"
      "    children_fixed_height = 3;"
      "//--------------------------------------------------------"
      "   :boxed_column {label = \"間隔\";"
      "    :edit_box{"
      "     label = \"主曲線\";"
      "     edit_width = 10;"
      "     key = \"lwcn-distance\"; }"
      "    :edit_box{"
      "     label = \"計曲線\";"
      "     edit_width = 10;"
      "    key = \"hicn-distance\"; }"
      "    }//boxed_column"
      "   :boxed_column { label =\"高さ付けする方向\";"
      "    :radio_column{ key =\"direction-option\";"
      "     :radio_button{ label =\"低い→高い\";"
      "                    key=\"1\";}"
      "     :radio_button{ label =\"高い→低い\";"
      "                    key=\"-1\";}"
      "    :spacer { height = 0;}"
      "    }//radio_column"
      "   }//boxed_column"
      "//--------------------------------------------------------"
      "   :boxed_column { label =\"レイヤー設定\";"
      "   :popup_list { label = \"主曲線\";"
      "key = \"lwcn_layer_lst\";"
      "     allow_accept = true;"
      "     height = 2;"
      "     width = 30;"
      "     multiple_select = false;"
      "    }//list_box  "
      "   :popup_list { label = \"計曲線\";"
      "key = \"hicn_layer_lst\";"
      "     allow_accept = true;"
      "     height = 2;"
      "     width = 30;"
      "     multiple_select = false;"
      "    }//list_box  "
      "   }//boxed_column"
      "  }//colmun"
      " }//row"
      "ok_cancel;"
      "}"
     )
    (write-line str file-open)
 );end make dcl
 (close file-open)

 (setq lwcn_layer_lst (cons "-" (table-names "LAYER")))
 (setq hicn_layer_lst (cons "-" (table-names "LAYER")))

 (new_dialog "setting_chelev" (setq dcl-id (load_dialog file-name)))
 (start_list "lwcn_layer_lst")
 (mapcar (function add_list) lwcn_layer_lst)
 (end_list)

 (start_list "hicn_layer_lst")
 (mapcar (function add_list) hicn_layer_lst)
 (end_list)


;ダイアログの初期値の設定
;主曲線のレイヤー設定
 (if (null *lwcn_layer*)
  (setq *lwcn_layer* "-")
 )
 (if (null (setq *lwcn_layer_num* (vl-position *lwcn_layer* lwcn_layer_lst)))
  (setq *lwcn_layer_num* 0)
 )
 (set_tile "lwcn_layer_lst" (rtos *lwcn_layer_num*))

;計曲線のレイヤー設定
 (if (null *hicn_layer*)
  (setq *hicn_layer* "-")
 )
 (if (null (setq *hicn_layer_num* (vl-position *hicn_layer* hicn_layer_lst)))
  (setq *hicn_layer_num* 0)
 )
 (set_tile "hicn_layer_lst" (rtos *hicn_layer_num*))

;主曲線の間隔設定
 (if (null *lwcn-distance*)
  (setq *lwcn-distance* 1.0)
 )
 (set_tile "lwcn-distance" (rtos *lwcn-distance* 2 0))

;計曲線の間隔設定
 (if (null *hicn-distance*)
  (setq *hicn-distance* 5.0)
 )
 (set_tile "hicn-distance" (rtos *hicn-distance* 2 0))

;高さ付け方向の設定
 (if (null *direction-option*)
  (setq *direction-option* 1.0)
 )
 (set_tile "direction-option" (rtos *direction-option* 2 0))

 (action_tile "accept" "(chelev_getvar)(done_dialog 1)")
 (action_tile "cancel" "(done_dialog 0)")
 (setq act (start_dialog))
 (unload_dialog dcl-id)
   (cond
     ((= Act 0)(princ "キャンセルキーを押しました"))
     ((= Act 1)(princ "OKを押しました"))
   );cond
 (vl-file-delete file-name)
 (princ)
)


(defun chelev_getvar ( / )
  (setq *hicn-distance* (atof (get_tile "hicn-distance")))
  (setq *lwcn-distance* (atof (get_tile "lwcn-distance")))
  (setq *direction-option* (atof (get_tile "direction-option")));低→高のとき「1」、高→低のとき「-1」
  (setq *lwcn_layer* (nth (atoi (get_tile "lwcn_layer_lst")) lwcn_layer_lst))
  (setq *hicn_layer* (nth (atoi (get_tile "hicn_layer_lst")) hicn_layer_lst))
)


;[table-name]:"LAYER","LTYPE","VIEW","STYLE","BLOCK","UCS","APPID","DIMSTYLE","VPORT"
;[return]:table-name list
(defun table-names (table-name / tbl lst)
 (if (setq tbl (tblnext table-name T))
  (progn
   (setq lst (list (cdr (assoc 2 tbl))))
   (while (setq tbl (tblnext table-name))
     (setq lst (append lst (list (cdr (assoc 2 tbl)))))
   )
  )
 )
)
~~~
</details>

### 高さを与える処理
ここからはダイアログの処理ではなく、ポリラインを操作する処理です。
前述の通り、以下の流れです。
2.高さを付ける等高線をまたぐように2点クリックで直線を指示
3.基準となる高さを入力
4.高さとレイヤーをそれぞれ変更
5.繰り返し(2～5）

レイヤー情報、主曲線、計曲線の間隔などの設定はダイアログボックスから行います。
基準となる高さは、最初に交差したポリラインを参照するか、しないで手入力するか決めて実行します。

そこまで難しくないとも思うので、説明は割愛します。
途中、ポリラインから高さを取得するサブルーチンがあります。

<details><summary>高さを与える処理のコード</summary>

~~~LISP
(vl-load-com)

(defun C:CHELEV (/ Old_Os Old_Cm Old_lay ss1 elevation interval i pt1 pt2 ename ent)

;error function
;setup
 (setq Old_Os (getvar "OSMODE");オブジェクトスナップの保存
       Old_Cm (getvar "CMDECHO");エコーモードの保存
       Old_lay (getvar "CLAYER");現在の画層
 )

;error
 (defun *error* (msg)
  (setvar "OSMODE" Old_Os);オブジェクトスナップの戻し
  (setvar "CMDECHO" Old_Cm);エコーモードの戻し
  (setvar "CLAYER" Old_lay);画層の戻し
  (princ)
 )

 (setvar "OSMODE" 0)
 (setvar "CMDECHO" 0)


;main function
 (chelev_setvar)

 (while (setq pt1 (getpoint "\nフェンスラインの基点を選択<Enterで終了>:"))
  (setq pt2 (getpoint "\n終点を選択:"))

  (command "_LINE" pt1 pt2 "")

  (setq ss1 (ssdel (entlast) (ssget "_F" (list pt1 pt2) '((100 . "AcDbPolyline,AcDb2dPolyline,AcDbLine")))))
  (setq i 0)
  (setq ent (entget (ssname ss1 i)))

;オブジェクトから高さを読み取り
  (setq coordinates (get:coords (vlax-ename->vla-object (ssname ss1 i))))
  (if (null (setq start-elevation (caddr (car coordinates))))
    (setq start-elevation (cdr (assoc 38 ent)))
  )

  (setq text (strcat "\n基準の等高線の高さを入力[" (rtos start-elevation 2 1) "]: "))
  (if (null (setq elevation (getreal text)))
    (setq elevation start-elevation)
  )


;高さ付けのループ処理
  (while (<= i (- (sslength ss1) 1))
    (progn
      (setq ename (ssname ss1 i))
      (if (integerp (/ elevation *hicn-distance*))
       (setq layer
             (if (vl-position *hicn_layer* (table-names "LAYER"))
               *hicn_layer*
               nil
             )
       )
       (setq layer
             (if (vl-position *lwcn_layer* (table-names "LAYER"))
               *lwcn_layer*
               nil
             )
       )
      )
      (if layer
       (command "_CHANGE" ename "" "_P" "_LA" layer "")
      )

      (command "_CHANGE" ename "" "_P" "_E" elevation "")

      (setq i (1+ i))
      (setq elevation (+ elevation (* *lwcn-distance* *direction-option*)))
    )
  );while

  (command "._erase" (entlast)"")

 );while

 (setvar "OSMODE" Old_Os)
 (setvar "CMDECHO" Old_Cm)

 (princ)
)


;sub function
;https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/simplest-way-to-get-coordinates/td-p/10299650
;by Miljenko Hatlak

(defun get:coords (e / take drop_first_n nthcdr points2d points3d)
	(defun take (amount lst / ret) (repeat amount (setq ret (cons (car lst) (take (1- amount) (cdr lst))))))
	(defun drop_first_n (n alist) (nthcdr n alist))
	(defun nthcdr (arg lst / ret) (setq ret lst) (if (> arg 0)(repeat arg (setq ret (cdr ret)))))
	(defun points3d (lst / ret) (while lst (setq ret (cons (take 3 lst) ret) lst (drop_first_n 3 lst))) (reverse ret))
	(defun points2d (lst / ret) (while lst (setq ret (cons (take 2 lst) ret) lst (drop_first_n 2 lst))) (reverse ret))
	(cond ((= (type e) 'ENAME) (setq e (vlax-ename->vla-object e))))
	(cond
		((or (= (vlax-get e 'Objectname) "AcDb2dPolyline")(= (vlax-get e 'Objectname) "AcDb3dPolyline"))
			(points3d (vlax-get e 'Coordinates))
		)
		((= (vlax-get e 'Objectname) "AcDbPolyline")
			(points2d (vlax-get e 'Coordinates))
		)
		((= (vlax-get e 'Objectname) "AcDbLine")
			(list (vlax-get e 'StartPoint)(vlax-get e 'EndPoint))
		)
	)
)


;数値が整数かどうか判定する。
(defun integerp (num /)
 (if (numberp num)
  (= 0 (- (atof (rtos num 2 10)) (fix num)))
 )
)

~~~
</details>

### 最終的出来たコード

最後に私のほうで使っているものを載せます。

<details><summary>最終的にできたコード</summary>

~~~LISP
(vl-load-com)

(defun C:CHELEV (/ Old_Os Old_Cm Old_lay ss1 elevation interval i pt1 pt2 ename ent)

;error function
;setup
 (setq Old_Os (getvar "OSMODE");オブジェクトスナップの保存
       Old_Cm (getvar "CMDECHO");エコーモードの保存
       Old_lay (getvar "CLAYER");現在の画層
 )

;error
 (defun *error* (msg)
  (setvar "OSMODE" Old_Os);オブジェクトスナップの戻し
  (setvar "CMDECHO" Old_Cm);エコーモードの戻し
  (setvar "CLAYER" Old_lay);画層の戻し
  (princ)
 )

 (setvar "OSMODE" 0)
 (setvar "CMDECHO" 0)


;main function
 (chelev_setvar)

 (while (setq pt1 (getpoint "\nフェンスラインの基点を選択<Enterで終了>:"))
  (setq pt2 (getpoint "\n終点を選択:"))

  (command "_LINE" pt1 pt2 "")

  (setq ss1 (ssdel (entlast) (ssget "_F" (list pt1 pt2) '((100 . "AcDbPolyline,AcDb2dPolyline,AcDbLine")))))
  (setq i 0)
  (setq ent (entget (ssname ss1 i)))

;オブジェクトから高さを読み取り
  (setq coordinates (get:coords (vlax-ename->vla-object (ssname ss1 i))))
  (if (null (setq start-elevation (caddr (car coordinates))))
    (setq start-elevation (cdr (assoc 38 ent)))
  )

  (setq text (strcat "\n基準の等高線の高さを入力[" (rtos start-elevation 2 1) "]: "))
  (if (null (setq elevation (getreal text)))
    (setq elevation start-elevation)
  )


;高さ付けのループ処理
  (while (<= i (- (sslength ss1) 1))
    (progn
      (setq ename (ssname ss1 i))
      (if (integerp (/ elevation *hicn-distance*))
       (setq layer
             (if (vl-position *hicn_layer* (table-names "LAYER"))
               *hicn_layer*
               nil
             )
       )
       (setq layer
             (if (vl-position *lwcn_layer* (table-names "LAYER"))
               *lwcn_layer*
               nil
             )
       )
      )
      (if layer
       (command "_CHANGE" ename "" "_P" "_LA" layer "")
      )

      (command "_CHANGE" ename "" "_P" "_E" elevation "")

      (setq i (1+ i))
      (setq elevation (+ elevation (* *lwcn-distance* *direction-option*)))
    )
  );while

  (command "._erase" (entlast)"")

 );while

 (setvar "OSMODE" Old_Os)
 (setvar "CMDECHO" Old_Cm)

 (princ)
)


;sub function
;https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/simplest-way-to-get-coordinates/td-p/10299650
;by Miljenko Hatlak

(defun get:coords (e / take drop_first_n nthcdr points2d points3d)
	(defun take (amount lst / ret) (repeat amount (setq ret (cons (car lst) (take (1- amount) (cdr lst))))))
	(defun drop_first_n (n alist) (nthcdr n alist))
	(defun nthcdr (arg lst / ret) (setq ret lst) (if (> arg 0)(repeat arg (setq ret (cdr ret)))))
	(defun points3d (lst / ret) (while lst (setq ret (cons (take 3 lst) ret) lst (drop_first_n 3 lst))) (reverse ret))
	(defun points2d (lst / ret) (while lst (setq ret (cons (take 2 lst) ret) lst (drop_first_n 2 lst))) (reverse ret))
	(cond ((= (type e) 'ENAME) (setq e (vlax-ename->vla-object e))))
	(cond
		((or (= (vlax-get e 'Objectname) "AcDb2dPolyline")(= (vlax-get e 'Objectname) "AcDb3dPolyline"))
			(points3d (vlax-get e 'Coordinates))
		)
		((= (vlax-get e 'Objectname) "AcDbPolyline")
			(points2d (vlax-get e 'Coordinates))
		)
		((= (vlax-get e 'Objectname) "AcDbLine")
			(list (vlax-get e 'StartPoint)(vlax-get e 'EndPoint))
		)
	)
)


;数値が整数かどうか判定する。
(defun integerp (num /)
 (if (numberp num)
  (= 0 (- (atof (rtos num 2 10)) (fix num)))
 )
)


(defun chelev_setvar (/ file-open file-name str dcl-id hicn_layer_lst lwcn_layer_lst)
 (setq file-open (open (setq file-name (vl-filename-mktemp "" "" ".dcl")) "w"))
 (foreach str
    '("// chelev.dcl for use in chelev.lsp  //"
      "setting_chelev :dialog"
      "{ label = \"等高線の設定\";"
      "//--------------------------------------------------------"
      " : row {"
      "  : column {//colmun1_START"
      "    children_fixed_height = 3;"
      "//--------------------------------------------------------"
      "   :boxed_column {label = \"間隔\";"
      "    :edit_box{"
      "     label = \"主曲線\";"
      "     edit_width = 10;"
      "     key = \"lwcn-distance\"; }"
      "    :edit_box{"
      "     label = \"計曲線\";"
      "     edit_width = 10;"
      "    key = \"hicn-distance\"; }"
      "    }//boxed_column"
      "   :boxed_column { label =\"高さ付けする方向\";"
      "    :radio_column{ key =\"direction-option\";"
      "     :radio_button{ label =\"低い→高い\";"
      "                    key=\"1\";}"
      "     :radio_button{ label =\"高い→低い\";"
      "                    key=\"-1\";}"
      "    :spacer { height = 0;}"
      "    }//radio_column"
      "   }//boxed_column"
      "//--------------------------------------------------------"
      "   :boxed_column { label =\"レイヤー設定\";"
      "   :popup_list { label = \"主曲線\";"
      "key = \"lwcn_layer_lst\";"
      "     allow_accept = true;"
      "     height = 2;"
      "     width = 30;"
      "     multiple_select = false;"
      "    }//list_box  "
      "   :popup_list { label = \"計曲線\";"
      "key = \"hicn_layer_lst\";"
      "     allow_accept = true;"
      "     height = 2;"
      "     width = 30;"
      "     multiple_select = false;"
      "    }//list_box  "
      "   }//boxed_column"
      "  }//colmun"
      " }//row"
      "ok_cancel;"
      "}"
     )
    (write-line str file-open)
 );end make dcl
 (close file-open)

 (setq lwcn_layer_lst (cons "-" (table-names "LAYER")))
 (setq hicn_layer_lst (cons "-" (table-names "LAYER")))

 (new_dialog "setting_chelev" (setq dcl-id (load_dialog file-name)))
 (start_list "lwcn_layer_lst")
 (mapcar (function add_list) lwcn_layer_lst)
 (end_list)

 (start_list "hicn_layer_lst")
 (mapcar (function add_list) hicn_layer_lst)
 (end_list)


;ダイアログの初期値の設定
;主曲線のレイヤー設定
 (if (null *lwcn_layer*)
  (setq *lwcn_layer* "-")
 )
 (if (null (setq *lwcn_layer_num* (vl-position *lwcn_layer* lwcn_layer_lst)))
  (setq *lwcn_layer_num* 0)
 )
 (set_tile "lwcn_layer_lst" (rtos *lwcn_layer_num*))

;計曲線のレイヤー設定
 (if (null *hicn_layer*)
  (setq *hicn_layer* "-")
 )
 (if (null (setq *hicn_layer_num* (vl-position *hicn_layer* hicn_layer_lst)))
  (setq *hicn_layer_num* 0)
 )
 (set_tile "hicn_layer_lst" (rtos *hicn_layer_num*))

;主曲線の間隔設定
 (if (null *lwcn-distance*)
  (setq *lwcn-distance* 1.0)
 )
 (set_tile "lwcn-distance" (rtos *lwcn-distance* 2 0))

;計曲線の間隔設定
 (if (null *hicn-distance*)
  (setq *hicn-distance* 5.0)
 )
 (set_tile "hicn-distance" (rtos *hicn-distance* 2 0))

;高さ付け方向の設定
 (if (null *direction-option*)
  (setq *direction-option* 1.0)
 )
 (set_tile "direction-option" (rtos *direction-option* 2 0))

 (action_tile "accept" "(chelev_getvar)(done_dialog 1)")
 (action_tile "cancel" "(done_dialog 0)")
 (setq act (start_dialog))
 (unload_dialog dcl-id)
   (cond
     ((= Act 0)(princ "キャンセルキーを押しました"))
     ((= Act 1)(princ "OKを押しました"))
   );cond
 (vl-file-delete file-name)
 (princ)
)

(defun chelev_getvar ( / )
  (setq *hicn-distance* (atof (get_tile "hicn-distance")))
  (setq *lwcn-distance* (atof (get_tile "lwcn-distance")))
  (setq *direction-option* (atof (get_tile "direction-option")));低→高のとき「1」、高→低のとき「-1」
  (setq *lwcn_layer* (nth (atoi (get_tile "lwcn_layer_lst")) lwcn_layer_lst))
  (setq *hicn_layer* (nth (atoi (get_tile "hicn_layer_lst")) hicn_layer_lst))
)


;[table-name]:"LAYER","LTYPE","VIEW","STYLE","BLOCK","UCS","APPID","DIMSTYLE","VPORT"
;[return]:table-name list
(defun table-names (table-name / tbl lst)
 (if (setq tbl (tblnext table-name T))
  (progn
   (setq lst (list (cdr (assoc 2 tbl))))
   (while (setq tbl (tblnext table-name))
     (setq lst (append lst (list (cdr (assoc 2 tbl)))))
   )
  )
 )
)



~~~

</details>

## 雑記

『「等高線標高設定/入力」をAutoCADで使えるようにAutoLISPで作ってみる。』
というよりかは、ダイアログをテンポラリフォルダに作成してそれを呼び出す処理の説明になってしまいました。
テンポラリフォルダに作って消す処理とプルダウンリストの処理が得られたのは非常に有意義でした。
「.lsp」ファイルだけ渡せば済むので、実行が非常に楽。


V-nasなら最初からできますし、APS-ZEというソフトもあるのでそこまで必要ではないです。
Civil3DよりAutoCADのライセンスが多いことが普通なのと、等高線に高さを付ける簡易な作業はBIM/CIMを行う高度な設計者/オペレーターである必要もないので、あれば助かるのかなあ程度です。
何より私がV-nas使えない。