初心者向けのLISP解説、より使いやすくなるようにLISPを修正します。
オブジェクトの選択方法を変更してみましょう
前回までのLISP(以下、plusone.lspと表記)は、「整数だけの文字オブジェクトを選択」して実行する必要がありました。でも、実際の操作で、確認しながら選択するのはちょっと面倒です。それに、1つずつではなく一度に複数の文字を処理できたら、もっと使いやすくなるはず。というわけで、選択するオブジェクトの種類や中身を気にすることなく、しかも複数を一度に処理できるように変更してみましょう。
次の文字列をコピーして、コマンドラインに貼り付けてリターンを押してみてください。
(ssget '((0 . "TEXT")))
コマンドラインに「オブジェクトを選択:」と表示されたら、線分・文字・ブロックなど一度に選択します。そうすると、1行文字オブジェクトのみ、表示がハイライトされます。そのままリターンを押すと、オブジェクト選択が終了します。
ssget関数は、引数がない場合は選択した複数のオブジェクトを1つの選択セットとして取得しますが、前回説明したDXFグループコードを引数に指定すると、プロパティをキーにしてフィルタリング選択することができます。これを使って、オブジェクトの種類を気にすることなく選択できるコマンドに変更してみましょう。
どうすればssget関数の選択セットを、plusone.lspで使えるようにできるしょうか。
plusone.lspでは、ensel関数でオブジェクトを1つ選択して図形名を取得します。なので、選択セットの場合もそれぞれから図形名を取得できれば、その先は同じコードが使えそうです。選択セットから図形名を得るには、ssname関数を使います。そしてこの関数を使用する場合、選択セットの数をカウントするsslength関数と、決められた数だけ式を繰り返すrepeat関数を一緒に使うことが多いです。
ssget関数を使用して、繰り返し図形名を取り出すコードは、次の通りです。
(setq ss (ssget '((0 . "TEXT"))))
(setq numss (sslength ss))
(setq ssindex 0)
(repeat numss
(setq ename (ssname ss ssindex))
(setq ssindex (+ 1 ssindex))
) ;repeat終わり
5行目でenameを取得しているので、この次にplusone.lspの(setq elist (entget ename))以下を使うことができます。なお、最終行にある;(半角セミコロン)に続く文字列はコメントといい、コードに影響しません。このように説明などを書いておくと、わかりやすくなります。
文字の内容によって処理を分けるには?
これで、選択した文字オブジェクトの図形名を取得できました。しかし、その内容が整数とは限りません。次はif関数を使って、条件を満たすかどうかを判定して分岐します。
試しに、plusoneコマンドで整数ではない文字を選択するとどうなるでしょう。文字が1に変更されてしまいます。実はatoi関数は、整数に変換できない文字列を引数にした場合、0を返すため、整数以外を選択すると0+1となり1に変更されます。これを利用して判定してみましょう。
上のように、文字内容を整数→文字列に変換して変換前と比較することで、内容が整数かどうかを判定できます。plusone.lspの中の変数を使用してコードにすると
(= (itoa num) txtval)
となります。
これを条件式として、if関数を使いましょう。if関数の引数は以下の通りです。
(if 条件式 満たす場合の式 [満たさない場合の式])
条件式には(= (itoa num) txtval)が入ります。そのあとに、条件を満たす場合の処理と満たさない場合の処理(省略可能)を記述します。ここで注意しなければいけないのは、それぞれに入れることができるのは、1まとまりの式だということです。「式」というのは、()で囲まれたまとまりです。plusone.lspは、大きくはdefun関数の式ですが、中にはいくつも式があるため、そのまま 満たす場合の式 に当てはめることはできません。そのため、progn関数を使い、コードの7行目から11行目を複数の式を1つにまとめます。
さあ、組み合わせて完成させましょう!
これで、
- 複数オブジェクトを
- 文字内容を気にすることなく
選択する準備ができました。plusone.lspを修正していきましょう。
まずは、ローカル変数にssget関数での選択に使うss numss ssindexを追加します。
(defun c:plusone ( / ename elist dpair txtval num Newnum Newelist Newtxtval Newpair)
↓
(defun c:plusone ( / ss numss ssindex ename elist txtval dpair num Newnum Newelist Newtxtval Newpair)
次に、entsel関数による選択をssget関数に置き換えます。
(setq ename (car (entsel)))
↓
(setq ss (ssget '((0 . "TEXT"))))
(setq numss (sslength ss))
(setq ssindex 0)
(repeat numss
(setq ename (ssname ss ssindex))
変数enameを取得してからのコードを、そこに続けます。
(setq elist (entget ename))
(setq dpair (assoc 1 elist))
(setq txtval (cdr dpair))
(setq num (atoi txtval))
文字列を整数に変換したところで、if関数で条件を満たすかどうかの判定式を追加します。
(if (= (itoa num) txtval) ;ifによる分岐
条件を満たす場合の処理を、progn関数で1つの式にします。
(progn
(setq Newnum (+ 1 num))
(setq Newtxtval (itoa Newnum))
(setq Newpair (cons 1 Newtxtval))
(setq Newelist (subst Newpair (assoc 1 elist) elist))
(entmod Newelist)
) ;progn終わり
条件を満たさない場合の処理はないので、if関数の式を閉じます。
) ;if終わり
次のオブジェクトを取り出すために、変数ssindexに1を足して、repeat関数の式を閉じます。
(setq ssindex (+ 1 ssindex))
) ;repeat終わり
defun関数を閉じてコード終了です。
(princ)
) ;defun終わり
エディタ内のテキストをロードして、修正したplusoneコマンドを実行してみましょう。整数以外の文字を含む複数のオブジェクトを選択しても、正しく処理されましたか。
これで、今回のLISP解説は終了です。自分でもいろいろ作れそうな感じがしませんか。
著者について
Twitterでフォローする ウェブサイトを見る このライターによる他のコンテンツ