2010年2月18日木曜日

Microsoft Visual Studio 2008 Standard Edition を使用してのカスタマイズ(AutoCAD 2010)

先日より、.NET の勉強を始めています。

初心者ということもあり、無償版の Microsoft Visual Studio 2008 Express Edition を使用していたのですが、AutoCAD 2010 のカスタマイズで使えるけどサポートされないということと、デバッグのやり方が分からなかったので、思い切って Microsoft Visual Studio 2008 Standard Edition を購入しました。

そこで早速、他のサンプルを試してみようと思います。


acad_mdg.chm を適当に探して、以下のサンプルをやってみることにしました。

------------------------------------------
この例では、Add メソッドを使用して、acad.dwt 図面テンプレート ファイルに基づいて新しい図面を作成します。

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Runtime

<CommandMethod("NewDrawing", CommandFlags.Session)> _
Public Sub NewDrawing()
  '' Specify the template to use, if the template is not found
  '' the default settings are used.
  Dim strTemplatePath As String = "acad.dwt"

  Dim acDocMgr As DocumentCollection = Application.DocumentManager
  Dim acDoc As Document = acDocMgr.Add(strTemplatePath)
  acDocMgr.MdiActiveDocument = acDoc
End Sub
------------------------------------------
  1. まずは、Visual Studio 2008 を起動します。
  2. [ファイル] - [新しいプロジェクト] を選択して、[新しいプロジェクト] ダイアログで [クラス ライブラリ] を選択して [OK] ボタンをクリックです。(プロジェクト名は、NewDrawing にしました。)
  3. 例のごとく、サンプル プログラムを Class1.vb にカット&ペーストして以下のようにします。

    Imports Autodesk.AutoCAD.ApplicationServicesImports
    Autodesk.AutoCAD.DatabaseServices
    Imports Autodesk.AutoCAD.Runtime

    Public Class Class1
        <CommandMethod("NewDrawing", CommandFlags.Session)> _
        Public Sub NewDrawing()
            '' Specify the template to use, if the template is not found
            '' the default settings are used.
            Dim strTemplatePath As String = "acad.dwt"

            Dim acDocMgr As DocumentCollection = Application.DocumentManager
            Dim acDoc As Document = acDocMgr.Add(strTemplatePath)
            acDocMgr.MdiActiveDocument = acDoc
        End Sub
    End Class

  4. 最初は [ソリューション エクスプローラ] に「参照設定」が表示されていないので、[すべてのファイルを表示] ボタンをクリックして表示します。
  5. そうすると、以下のように「参照設定」が表示されました。
  6. [参照設定] を右クリックして、[参照の追加] をクリックです。 例のごとく、[参照の追加] ダイアログで [参照] タブを選択して、C:\Program Files\AutoCAD 2010 フォルダにある AcDbMgd.dll を選択して [OK] です。
  7. 同じように、AcMgd.dll も追加します。(このサンプルでは、AcCui.dll は追加する必要はないようです。)
  8. [ファイル] - [すべてを保存] を選択して NewDrawing というソリューション名で保存しました。
では、さっそくデバッグしてみましょう。
  1. まずは、デバッグ時に AutoCAD を起動するための設定です。
    [ソリューション エクスプローラ] で NewDrawing を右クリックして [プロパティ] を選択します。
  2. デバッグを選択して、[外部プログラムの開始] に C:\Program Files\AutoCAD 2010\acad.exe を設定します。(設定したあと、[選択されたファイルを上書き保存] ボタンをクリックしておきました。)
  3. ここで、いよいよ [デバッグ開始] ボタンをクリックです。
  4. AutoCAD 2010 が起動してきましたが、「アプリケーションのコンポーネントで、ハンドルされていない例外が発生しました。...」というエラーが表示されました。
    そのため、一旦デバッグ中止です。
  5. どうやら、デバッグするためには参照設定した acdbmgr と acmgd の参照プロパティーで [ローカル コピー] を True から False に変更しなきゃいけないみたいです。
    なので、そのとおり変更しました。
  6. ここで、再度 [デバッグ開始] ボタンをクリックです。
    AutoCAD 2010 が起動してきて、今度はエラーなしです。
  7. NETLOAD コマンドを実行して、Debug フォルダにある NewDrawing.dll をロードします。
  8. NewDrawing コマンドを実行すると、問題なく新規図面が開かれました。
試しにブレーク ポイントを「Dim strTemplatePath As String = "acad.dwt"」の行に設定して NewDrawing コマンドを実行したところ、ちゃんとその行で止まりました。

動画も作りました。

2010年2月10日水曜日

よく使う関数を使用してのサンプル ファイル(AutoCAD 2010)

 以下内容を拡張子 lsp という名前で保存し、ロードして sample1 コマンドを実行すると、寸法が存在すれば「寸法」画層に移動されます。(「寸法」画層が存在しなければ、作成されます。)

(defun c:sample1 (/ ss ss_len)
; コマンド名を sample1 とし、他に影響がないように、変数 ss ss_len をローカル変数にします。

    (if (setq ss (ssget "X" '((0 . "DIMENSION"))))
    ; もし、寸法が存在すれば以下の progn で囲まれた部分を実行します。
      (progn
        (if (not (tblsearch "LAYER" "寸法"))
        ; もし、「寸法」という画層がなければ以下を実行します。(「寸法」画層を作成します。)
        (command "_.LAYER" "N" "寸法" "")
      )

        ; すべての寸法の画層を「寸法」に変更します。
        (command "_.CHANGE" ss "" "P" "LA" "寸法" "")

        ; 寸法の数を求めます。
        (setq ss_len (sslength ss))

        ; 移動した寸法の数を表示します。strcat 関数は、文字列同士をくっつける関数です。
        (alert (strcat (itoa ss_len) " 個の寸法を「寸法」画層に移動しました。"))
      )
    ; もし、寸法が存在しなければ以下の progn で囲まれた部分を実行します。
      (progn
        (alert "寸法がひとつもありませんでした。")
      )
    )
    (princ)
)

2010年2月9日火曜日

tblnext、tblsearch 関数(AutoCAD 2010)

シンボル テーブル(VPORT、LINETYPE、LAYER、STYLE、DIMSTYLE、BLOCKS、APPID)の情報を取得するために使用する関数です。

図面中に「ある画層は存在する?」、「どんな寸法スタイル名がある?」などを確認したいときに使用します。

使用例
(tblname <シンボル テーブル> [rewind])
<シンボル テーブル> で指定したシンボル テーブルに設定されているものを順番に返します。
[rewind] の部分は省略可能です。T を入れると、このシンボル テーブルの最初の設定が返されます。
例えば、以下のような使い方になります。

(tblnext "LAYER" T)    ; T を最後に入れていますので、図面中の最初の画層の情報が返されます。
(tblnext "LAYER")    ; 図面中の 2 番目の画層の情報が返されます。

このあと、(tblnext "LAYER")  を繰り返して実行すると、3 番目、4 番目、・・・と順番に情報が返されます。
もう画層がなくなったときは、nil が返されます。


(tblsearch <シンボル テーブル> <名前> [setnext])
<シンボル テーブル> で指定したシンボル テーブルに <名前> で指定した名前が存在すれば、その情報が返され、なければ nil が返されます。
例えば、以下を実行したとします。

(tblsearch "DIMSTYLE" "TEST")

図面中に、TEST という寸法スタイルが存在すれば、その情報が返され、なければ nil が返されます。

[setnext] の部分は省略可能です。T を入れると、このあとに tblnext 関数を実行すると、tblsearch 関数で返されたシンボル テーブルのつぎのシンボル テーブルの情報が返されるようになります。

2010年2月7日日曜日

defun 関数(AutoCAD 2010)

自分で新たに作成する関数を定義する関数です。

使用例
(defun sym (<引数> / <ローカル変数>)
    
)

sym は自分で新たに作成する関数の名前です。
例えば、test にすると、AutoCAD 上 あるいは AutoLISP のプログラム内で (test) と入力すると実行されます。
また、c:test にすると、コマンドとして定義されることになります。そのため、AutoCAD 上で test と入力すると実行されます。AutoLISP のプログラム内では (c:test) と入力すると実行されます。

<引数> は関数を実行する時の引数です。

<ローカル変数>
ローカル変数とは、この関数の実行中にのみ設定される変数で、この関数が完了したあとは設定がなくなる変数です。(ローカル変数の逆は、グローバル変数です。)
例えば、以下の 2 つの例で違いを見てみましょう。

<例 1>
(defun c:test1 (/ pt1 pt2)
    (setq pt1 '(0.0 0.0 0.0))
    (setq pt2 '(100.0 100.0 0.0))
    (command "_.LINE" pt1 pt2 "")
    (princ)
)

<例 2>
(defun c:test2 ()
    (setq pt1 '(0.0 0.0 0.0))
    (setq pt2 '(100.0 100.0 0.0))
    (command "_.LINE" pt1 pt2 "")
    (princ)
)

例 1 の test1 コマンドを実行したあとは、変数 pt1 と pt2 が nil に戻ります。
例 2 の test2 コマンドを実行したあとは、変数 pt1 には (0.0 0.0 0.0) が、変数 pt2 には (100.0 100.0 0.0) が設定されたままになります。
(AutoCAD のコマンド ラインに !pt1 や !pt2 と入力することにより確認できます。)

ローカル変数であれば、他の関数実行時にその設定が影響することはありません。
グローバル変数は、他の関数で同じ変数名を使用していると、その設定が影響することがあります。

2010年2月6日土曜日

ssget、ssname、sslength 関数(AutoCAD 2010)

 ユーザにオブジェクトを複数選択させたり、ある条件にあったオブジェクトを複数選択してそれらの情報を取得したいことがあると思います。

例えば、ユーザが選択した複数のオブジェクトの画層、色、線種などの情報を取得したり、図面の中の寸法図形のみを抽出して、その画層などの上方を取得したいといったような時です。

使用例
(ssget)
コマンド ラインに「オブジェクトを選択:」と表示されます。
ここで、オブジェクトを選択すると、選択したオブジェクトの選択セットが返されます。


(ssget "X" <条件>)
<条件> の部分に当てはまるすべてのオブジェクトを含んだ選択セットが返されます。
例えば、(ssget "X" '((0 . "DIMENSION"))) と実行すると、図面中に含まれるすべての寸法図形を含んだ選択セットが返されます。


(ssname ss index)
選択セット ss の index で指定した番号の要素の図形名が返されます。
index は、0 から始まります。

例えば、以下を実行したとします。

(setq ss (ssget "X" '((0 . "LINE"))))
(setq obj_1 (ssname ss 0))
(setq obj_2 (ssname ss 1))

そうすると、変数 ss に図面中のすべての線分を含んだ選択セットが設定され、変数 obj_1 にはその選択セットの 最初の線分の図形名、変数 obj_2 にはその選択セットの 2 番目の図形名が設定されます。


(sslength ss)
選択セット ss に含まれるオブジェクトの数が返されます。

If、while、cond 関数(AutoCAD 2010)

プログラムを作成するとき、「もし~なら***、そうでないなら***」とか「~の間は繰り返す」とか「A のときは***、B のときは***、C のときは***」といった条件文を作成する必要がでてくると思います。

使用例
(if <条件>
    <条件> が真の場合に実行する内容
    <条件> が偽の場合に実行する内容
)

例えば以下の内容を test.lsp というファイルとして保存し、ロードして test コマンドを実行したとします。

(defun C:TEST ()
    (setq a (getint "\n数値を入力:"))
    (if (>= a 10)
        (alert "10 以上の数値が入力されました。")
        (alert "10 未満の数値が入力されました。")
    )
)

そうすると、「数値を入力:」に対して 10 以上の数値を入力すると、「10 以上の数値が入力されました。」と表示され、10 未満の数値を入力すると、「10 未満の数値が入力されました。」と表示されます。

<注意!!>
<条件> が真 または 偽の場合に実行する内容が複数の場合は、progn でまとめる必要があります。以下に例を示します。

(defun C:TEST ()
    (setq a (getint "\n数値を入力:"))
    (if (>= a 10)
      (progn
        (alert "10 以上の数値が入力されました。")
        (alert "以上でコマンド終了です。")
      )
      (progn
        (alert "10 未満の数値が入力されました。")
        (alert "以上でコマンド終了です。")
      )
    )
)


(while <条件>
    <条件> に当てはまる場合に実行する内容
)

例えば以下の内容を test.lsp というファイルとして保存し、ロードして test コマンドを実行したとします。

(defun C:TEST ()
    (setq n 5)
    (while (> n 0)
        (alert "while 文実行中です。")
        (setq n (- n 1))
    )
)

そうすると、「while 文実行中です。」という表示が 5 回繰り返し表示されます。


(cond
    <条件1><条件1 の場合に実行する内容>
    <条件2><条件2 の場合に実行する内容>
    <条件3><条件3 の場合に実行する内容>
                    ・
                    ・
    T <どの条件にも当てはまらない場合に実行する内容>
)

例えば以下の内容を test.lsp というファイルとして保存し、ロードして test コマンドを実行したとします。

(defun C:TEST ()
    (setq a (getint "\n数値を入力:"))
    (cond
        ((= a 1) (alert "1 が入力されました。"))
        ((= a 2) (alert "2 が入力されました。"))
        ((= a 3) (alert "3 が入力されました。"))
        (T (alert "1 でも 2 でも 3 でもない数値が入力されました。"))
    )
)

そうすると、「数値を入力:」に対して 1(または 2 や 3)を入力すると、「1 (または 2 や 3)が入力されました。」と表示され、1、2、3 以外の数値を入力すると、「1 でも 2 でも 3 でもない数値が入力されました。」と表示されます。

2010年2月5日金曜日

entsel、entlast、entget 関数(AutoCAD 2010)

AutoCAD においてプログラムを作るとき、あるオブジェクトの情報を取得したくなることもあると思います。
例えば、あるオブジェクトの画層、色、線種、文字スタイル名、寸法スタイル名などがどのようになっているかを知りたいときなどです。

使用例
オブジェクトの情報を取得したいときは、まずはそのオブジェクトの「図形名」を取得して、それを元にオブジェクトの情報を取得することになります。

(entsel)
コマンド ラインに「オブジェクトを選択:」と表示されます。
ここで、オブジェクトを選択すると、選択したオブジェクトの「図形名」とクリックした座標値が返されます。

(entlest)
最後に作成したオブジェクトの「図形名」が返されます。

(entget (car (entsel)))
選択したオブジェクトの情報が返されます。
car 関数は、リストの最初の要素を返します。(car (entsel)) は、(entsel) の返り値の最初の要素を返すということになりますので、選択したオブジェクトの「図形名」が返されます。

(entget (car (entsel)) '("*"))
「 '("*")」が追加されていますが、これは、オブジェクトに追加されている拡張データの情報も取得したいときに追加します。「*」となっているのは、すべてのアプリケーション名に対する拡張データも取得するということです。
例えば、acadvideo というアプリケーション名の拡張データだけを取得するときは (entget (car (entsel)) '("acadvideo")) となります。

(entget (entlast))
(entget (entlast) '("*"))
最後に作成したオブジェクトの情報が返されます。
「 '("*")」を付けたときは、拡張データも返されます。

例えば、中心が 100,100 で、半径 50 の円を描き、(entget (entlast)) を実行すると、以下のような情報が返されます。

((-1 . <図形名: 7ffffb0b830>) (0 . "CIRCLE") (330 . <図形名: 7ffffb099f0>) (5 .
"1FB") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (100 .
"AcDbCircle") (10 100.0 100.0 0.0) (40 . 50.0) (210 0.0 0.0 1.0))

ちょっとだけ説明すると、

(0 . "CIRCLE") は、オブジェクトの種類が「円」
(8 . "0") は、画層が 0
(10 100.0 100.0 0.0) は、中心の座標値が 100.0,100.0,0.0
(40 . 50.0) は、半径が 50.0

ということを示してします。