2010年12月28日火曜日

.NET - 2 つの図面のシステム変数の違いを見つける(AutoCAD 2011)


『なんでか他の図面では問題ないんだけど、この図面だけ動きがおかしいんだよね』 なんてときには、図面に保存されているシステム変数の値が原因ってことがあります。
そんな時には、問題ない図面とシステム変数を比較してみるのがいいと思います。

ということで、簡単に比較できるようなツールを .NET の勉強も兼ねて作ってみました。


以前、2つの図面のシステム変数の比較(AutoCAD 2011) で、『.NET の ReadDwgFile メソッドを使用して、2つの図面のシステム変数の比較ができるかもと言ったのですが、チャレンジしてみたのですが、できませんでした。』って言ってたんですが、できました。

システム変数を取得するには Application.GetSystemVariable メソッドを使うしかないと思ってたんですが、Database Properties にシステム変数があるってことに気が付きました。

Database Properties にあるシステム変数をひとつずつ比較してみたので、結構時間がかかりましたが何とかできました。

ただ、システム変数 SKPOLY に関しては、ちゃんと判断できないみたいです。
というのは、SKPOLY には 0、1、2 のどれかの値が設定されるんですが、Database Properties では、True か False しか判断できないのです。
なので、0 は False で判断できるんですが、1 と 2 は両方とも True になっちゃうので、どちかわからないです。

しょうがないので、今回のプログラムでは、True のときは、『1 or 2』 って表示されるようにしました。


DLL ファイルは ここ にあります。

使い方とかは、動画を作ったので見てみてください。

2010年12月20日月曜日

.NET - 寸法図形の寸法スタイルを変更する(AutoCAD 2011)


昨日は、現在の寸法スタイルを変更する方法でしたね。
今日は、既に描かれている寸法図形を他の寸法スタイルに変更する方法を試してみたので、それを紹介したいと思います。

例えば、選択した寸法図形の寸法スタイルを「ACAD Video」という寸法スタイルの変えたいときは、こんな感じでできました。

'--------------------------------------------------------
<CommandMethod("test", "test4", CommandFlags.Modal + CommandFlags.UsePickSet)> _
Public Sub test4()

    Dim doc As Document = Application.DocumentManager.MdiActiveDocument
    Dim db As Database = doc.Database
    Dim ed As Editor = doc.Editor

    Using trans As Transaction = db.TransactionManager.StartTransaction
        Try
            ' PickFirst 選択セットを取得
            Dim psr As PromptSelectionResult = ed.SelectImplied()

            ' PickFirst 選択セットを取得できなかったときは、オブジェクトをオブジェクトを選択する
            If psr.Status <> PromptStatus.OK Then
                psr = ed.GetSelection()
            End If

            ' 何も選択されていなかったら、コマンドを終了する
            If psr.Status <> PromptStatus.OK Then
                Return
            End If

            ' ObjectIdCollection に選択したオブジェクトのオブジェクト ID を設定
            Dim objIdColl As New ObjectIdCollection(psr.Value.GetObjectIds())

            ' 選択したそれぞれのオブジェクトに対して実行
            For Each objId As ObjectId In objIdColl
                ' もし、寸法図形だったら、寸法スタイルを ACAD Vide に変更
                Dim ent As Entity = trans.GetObject(objId, OpenMode.ForRead)
                If TypeOf ent Is Dimension Then
                    Dim d1 As Dimension = ent

                    d1.UpgradeOpen()
                    d1.DimensionStyleName = "ACAD Video"
                End If
            Next

            trans.Commit()

        Catch ex As Autodesk.AutoCAD.Runtime.Exception
            Application.ShowAlertDialog(ex.Message)

        End Try
    End Using

End Sub
'--------------------------------------------------------

また、d1.DimensionStyleName = "ACAD Video" の代わりにこんな感じにしても OK でした。
'--------------------------------------------------------
Dim dimTbl As DimStyleTable = trans.GetObject(db.DimStyleTableId, OpenMode.ForRead)
Dim dimTblRec As DimStyleTableRecord = trans.GetObject(dimTbl("ACAD Video"), OpenMode.ForRead)

d1.DimensionStyle = dimTblRec.ObjectId
'--------------------------------------------------------

2010年12月19日日曜日

.NET - 現在の寸法スタイルを変更する/<優先スタイル> を破棄する(AutoCAD 2011)


今日は、.NET での寸法スタイルの変更についてです。

先日、<優先スタイル> を破棄したいといった質問をいただきました。

<優先スタイル> って知ってます?
例えば上の絵のように、現在の一部の設定(上の例では 「垂直方向の寸法値位置」(DIMTAD))が寸法スタイル(上の例では 「ACAD Video」)と違っているときに出てきます。

これをなくしたいってことなんですが、以下のようにやってもなくならないといった内容です。
db.Dimstyle = dimTblRec.ObjectId

※ db は Database、dimTblRec は DimStyleTableRecord
確かに、これを実行しても <優先スタイル> はなくなりませんでした。
なんででしょう???

いろいろ調べてみたんですが、<優先スタイル> ってどこにも覚えられていなみたいなんですよね。
どこかに覚えられていればそれを消せばいいかと思ってたんですが、それがどこにもなさそうなので、諦めかけてたんですが、やっと見つけました。

SetDimstyleData っていうのがあって、これを使うようです。
具体的にはこんな感じです。
'-----------------------------------------------------------------
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim db As Database = doc.Database

Using trans As Transaction = db.TransactionManager.StartTransaction
    Try
        Dim dimTbl As DimStyleTable = trans.GetObject(db.DimStyleTableId, OpenMode.ForRead)
        Dim dimTblRec As DimStyleTableRecord = trans.GetObject(dimTbl("ACAD Video"), OpenMode.ForRead)

        db.Dimstyle = dimTblRec.ObjectId
        db.SetDimstyleData(dimTblRec)

        trans.Commit()

    Catch ex As Autodesk.AutoCAD.Runtime.Exception
        Application.ShowAlertDialog(ex.Message)

    End Try
End Using
'-----------------------------------------------------------------

現在の寸法スタイルを変更するときもこれと同じで OK です。
(<優先スタイル> を破棄するときは、db.Dimstyle = dimTblRec.ObjectId がなくても OKです。)

なんで Dimstyle の設定だけではダメかというと、こういった理由になると思います。

DIM* といった寸法に関するシステム変数は、現在のデータベースに設定されています。
Dimstyle の設定を置き換えるように設定すると、現在の寸法スタイルは指定したものになりますが、他のシステム変数(例:DIMTAD など)は変わらずそのままです。
なので、<優先スタイル> も残ったままになります。

そのため、寸法スタイルの設定から、すべての DIM* というシステム変数を読み込む必要があります。
それをするのが SetDimstyleData ということです。

2010年12月13日月曜日

.NET - コマンドをキーボードから簡単に実行(AutoCAD 2011)


コマンドを実行するとき、通常はリボンやツールボタンから実行すると思うのですが、キーボードからも実行できます。

例えば、L と入力すると LINE[線分] コマンドが実行されたり、C と入力すると CIRCLE[円] コマンドが実行されたりします。
よく使うコマンドをこのようにキーボードから簡単に実行できたら、ひょっとしたらリボンやツールボタンから実行するよりも早く実行することができるかもしれませんね。

この L とか C というのは、コマンド エイリアスといって acad.pgp っているファイルで設定されています。

この acad.pgp ファイルは、テキスト ファイルなので、メモ帳などで編集することもできます。([管理]タブ-[エイリアス編集]をクリックすると、メモ帳で開かれます。)
また、[Express Tools]タブ-[Command Aliases] でも編集できます。

でもちょっと面倒なので、もう少し簡単に変更するプログラムを .NET で作ってみました。
(以前も http://blog.acadvideo.com/2010/09/net-autocad-2011.html で作ったのですが、あんまり使い勝手がよくなくて管理人自身も使わなくなってしまったので、改善してみました。)

使い方としては、こんな感じです。
  1. AVDO_SetAlias2.dll をダウンロードします。
  2. NETLOAD コマンドを実行して、AVDO_SetAlias2.dll をロードします。
    http://blog.acadvideo.com/2010/10/net-dll-autocad-2011.html の方法を使えば、AutoCAD 起動時に勝手にロードしてくれます。)
  3. AVDO_SetAlias2_Add コマンドを実行します。
    図面を開くたびに実行する必要があります。acaddoc.lsp に AVDO_SetAlias2_Add コマンドを実行するよう記述しておくと、図面を開いたときに勝手に実行してくれます。
  4. コマンド エイリアスを設定したいコマンドを実行します。
    コマンドを完了 または キャンセルします。
  5. AVDO_SetAlias2 コマンドを実行します。
※ http://blog.acadvideo.com/2010/09/net-autocad-2011.html では対応していなかった Express Tools の AutoLISP で作られたコマンドに対しても対応しました。


動画を作ってみたので見てみてください。
今回は、今までのように文字を入れるのではなく、しゃべってみました。
今回はじめてだったので、結構時間かかりましたが、慣れてくればしゃべった方が文字入れるより簡単かも。

2010年11月22日月曜日

.NET - 図面が保存されることを認識する方法(AutoCAD 2011)

以前いただいていた .NET の質問があったのですが、AUJ などがあってそのままにしていたのですが、AUJ も終わったので今日はそのことについて調べてみました。

内容としては、SAVE コマンドでの保存、上書き保存、図面 CLOSE 時の保存、VBA の SAVE メソッドでの保存 など図面が保存されることを認識したいということでした。

Document.CloseWillStart Event でコマンド名が SAVE のときに処理するようにしてみたところ、図面 CLOSE 時の保存、VBA の SAVE メソッドでの保存 では認識してくれないということでした。

試してみたところ、確かに CommandWillStart では 図面 CLOSE 時の保存、VBA の SAVE メソッドでの保存 は認識してくれませんでした。
どうやら、その時は何のコマンドも動いていないみたい(AutoCAD 内部の機能で保存されている?)で、そのために認識することが出来ていないようです。

ということで、何かないかなーと思って arxmgd.chm を眺めていたら、Database.BeginSave Event っていうのを見つけました。きっとこれだと思って試してみたところ、図面 CLOSE 時の保存、VBA の SAVE メソッドでの保存 でもちゃんと認識してくれました。

試してみたときのコードはこんな感じです。

'------------------------------------------------
' AddHandler を実行したことがあるかどうかの判断用の変数
Dim jHandler As Boolean = False

' イベントを登録するコマンド
<CommandMethod("AVDO_BeginSave", "AVDO_BeginSave", "AVDO_BeginSave", CommandFlags.Modal)> _
Public Sub AVDO_BeginSave()
    Dim doc As Document = Application.DocumentManager.MdiActiveDocument
    Dim db As Database = doc.Database

    ' AddHandler を複数回実行すると正しく動作しないため、実行されているかどうかを判断する。
    If jHandler = False Then
        AddHandler db.BeginSave, New DatabaseIOEventHandler(AddressOf BeginSave)

        jHandler = True
    End If

End Sub

' Save が開始されたときに実行する関数
Public Sub BeginSave(ByVal sender As Object, ByVal e As DatabaseIOEventArgs)

    MsgBox("保存が始まったよ!!")

End Sub
'------------------------------------------------

AVDO_BeginSave コマンドを実行するとイベントが登録されます。
それぞれの図面で実行しておく必要があるようなので、acaddoc.lsp に AVDO_BeginSave コマンドを実行するように記述しておけばいいと思います。

2010年11月15日月曜日

.NET - ラバーバンドを表示して 2 点目を指定する(AutoCAD 2011)

今日も .NET 質問いただいたことについて書こうと思います。

例えば、左下点と右上点をクリックして範囲を指定する(クリックした左下点と右上点の座標値を取得する)ときにラバーバンドを表示したいっといった内容です。

こんなとき、下の絵のようにラバーバンドが出てくれないと、この範囲を指定してますって感じに見えないですよね。

GetCorner メソッドを使えば、2 点目を指定するときにラバーバンドが出てきます。

管理人が確認したコードは、こんな感じです。

'--------------------------------------
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim ed As Editor = doc.Editor

Dim ppo As New PromptPointOptions("1点目を指定")
ppo.AllowNone = False

Dim ppr As PromptPointResult = ed.GetPoint(ppo)

If ppr.Status <> PromptStatus.OK Then
    Return
End If

Dim pt1 As Point3d = ppr.Value

Dim pco As New PromptCornerOptions(vbLf & "2点目を指定", pt1)
ppr = ed.GetCorner(pco)

If ppr.Status <> PromptStatus.OK Then
    Return
End If

Dim pt2 As Point3d = ppr.Value

MsgBox("1点目:" & pt1.ToString & vbLf & "2点目:" & pt2.ToString)
'--------------------------------------

2010年11月12日金曜日

.NET - WMF ファイルに書き出す(AutoCAD 2011)

先日いただいたアンケートで、SendStringToExecute 以外の方法で WMF ファイルに書き出したいというものをいただきました。

VBA では、(ThisDrawing.Export fName, ""WMF"", sset) とやってできてたそうです。(管理人は、VBA はやったことないので勉強になりました。)

ということで、ググッてみたところ、ひとつ方法を見つけました。
実際に試してみたところ、WMF ファイルに書き出すことができました。

WMF 以外にも EXPORT コマンドで書き出せるファイルはできるんじゃないかと思います。

ただ、GetObject を使っているので、AutoCAD が複数起動しているときは、必ず最初に起動した AutoCAD が認識されちゃいます。
そのため、あとから起動した AutoCAD 上で実行しても、最初に起動した AutoCAD で開いている図面が WMF に書き出されちゃうので注意が必要です。
何かいい方法はないのかなと思いつつも見つけ出せないでいます。もし、何かご存じの方がいて教えてくれたら嬉しいです。

試してみたコードは以下のような感じです。
(プロジェクトを作るときに、「AutoCAD Interop Common」 と 「AutoCAD Interop」 にチェックを入れて、Imports Autodesk.AutoCAD.Interop と Imports Autodesk.AutoCAD.Interop.Common を追記しました。)

'----------------------------------------------------------------------------
Public Sub AVDO_ExportWMF()
    Dim acadApp As Autodesk.AutoCAD.Interop.AcadApplication
    Dim acadDoc As Autodesk.AutoCAD.Interop.AcadDocument
    Dim ssetObj As AcadSelectionSet = Nothing

    ' 起動している AutoCAD のインスタンスを読み込む(AutoCAD が複数起動しているときは、最初に起動した AutoCAD で実行されるので注意!!)
    acadApp = GetObject(, "AutoCAD.Application.18.1")
    acadDoc = acadApp.ActiveDocument

    Try
        ' セレクションセットの作成
        ssetObj = acadDoc.SelectionSets.Add("AVDO_SSET01")

        ' すべてのオブジェクトをセレクションセットに入れる
        ssetObj.Select(AcSelect.acSelectionSetAll)    'puts everything into the selection set object

        ' WMF ファイルに書き出し
        acadDoc.Export("C:\Temp\test", "wmf", ssetObj)

    Catch ex As System.Exception
        Application.ShowAlertDialog(ex.Message)

    Finally
        ' ssetObj を削除(これをやっておかないと、2回目からエラーになってしまう。)
        ssetObj.Delete()

    End Try
End Sub
'----------------------------------------------------------------------------

2010年11月6日土曜日

.NET(サンプル プログラム) - 「ハッチングの境界をまとめて選択」(AutoCAD 2011)

先日いただいたアンケートの中に、『ハッチングの境界だけをまとめて選択したい』 というものがありました。

普通のコマンドでは、そのようなことはできないと思うので、.NET でコマンドをひとつ作ってみました。

DLL ファイル(AVDO_SelectBoundaries.dll)は こちら です。

使い方は、こんな感じです。

  1. NETLOAD コマンドを実行して、AVDO_SelectBoundaries.dll をロードします。
  2. AVDO_SelectBoundaries コマンドを実行します。
  3. ハッチングの境界を探す対象の図形を選択します。(すべての図形を対象とするときは、ALL と入力します。)
  4. Enter キーを押して、選択を完了します。

すると、ハッチングの境界だけが選択された状態になります。

※ PICKFIRST にも対応しているので、図形を選択したあとに AVDO_SelectBoundaries コマンドを実行しても OK です。
※ 自動調整ハッチングの境界を探しているので、自動調整でないハッチングの境界は対象外です。

動画も作りましたので、見てみてください。


ハッチングの境界を見つける方法は比較的簡単に分かったんですが、コマンド終了後に境界が選択された状態にしておく方法がなかなか見つけられなくて苦労しました。

SetImpliedSelection っていうのでできるっていうのをググッて見つけたんですが、arxmgd.chm にはそれが載ってないんですよね。それなのに、みんななんで知ってるんでしょうね。

また、SetImpliedSelection がうまく動かなかったときに、Twitter で takamoda さんや u2suzuki さんに助言いただき助かりました。どうもありがとうございました。

ということで、コードはこんな感じです。

'-----------------------------------------------
' CommandFlags.Redraw の記述がないと、コマンド終了後に図形が選択された状態にならないので、必ず付けること!!
<CommandMethod("AVDO_SelectBoundaries", "AVDO_SelectBoundaries", CommandFlags.Modal + CommandFlags.Redraw)> _
Public Sub AVDO_SelectBoundaries()
    Dim doc As Document = Application.DocumentManager.MdiActiveDocument
    Dim db As Database = doc.Database
    Dim ed As Editor = doc.Editor

    ' コマンド実行時に選択されている図形設定
    Dim result As PromptSelectionResult = ed.SelectImplied()

    ' コマンド実行時に、図形が選択されていないときは、図形を選択するよう要求
    If (result.Status <> PromptStatus.OK) Then
        result = ed.GetSelection()
    End If

    ' 図形が選択されているときは以下を実行する
    If (result.Status = PromptStatus.OK) Then
        Using trans As Transaction = db.TransactionManager.StartTransaction
            Try
                Dim bt As BlockTable = trans.GetObject(db.BlockTableId, OpenMode.ForRead)
                Dim btRec As BlockTableRecord

                Dim cSpace As BlockTableRecord = trans.GetObject(db.CurrentSpaceId, OpenMode.ForRead)
                If cSpace.Name = "*Model_Space" Then
                    btRec = trans.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite)
                Else
                    btRec = trans.GetObject(bt(BlockTableRecord.PaperSpace), OpenMode.ForWrite)
                End If

                'ハッチング境界用のコレクション
                Dim bObjIdColl As New ObjectIdCollection


                For Each selObj As SelectedObject In result.Value
                    Dim ent As Entity = trans.GetObject(selObj.ObjectId, OpenMode.ForRead)
                    Dim objIdColl As ObjectIdCollection = ent.GetPersistentReactorIds
                    For Each objId As ObjectId In objIdColl
                        Dim dbObj As DBObject = trans.GetObject(objId, OpenMode.ForRead)

                        'もし、リアクターのタイプが Hatch だったら、コレクションに追加
                        If TypeOf dbObj Is Hatch Then
                            bObjIdColl.Add(selObj.ObjectId)
                        End If
                    Next
                Next

                ' pickfirst set をクリア
                ed.SetImpliedSelection(New ObjectId(-1) {})

                ' コレクションを配列にダンプ
                Dim ids As ObjectId() = New ObjectId(bObjIdColl.Count - 1) {}
                bObjIdColl.CopyTo(ids, 0)

                ' pickfirst set を設定
                ed.SetImpliedSelection(ids)

            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                Application.ShowAlertDialog(ex.Message)

            End Try
        End Using
    End If
End Sub
'-----------------------------------------------

2010年10月27日水曜日

.NET - 文字列の長さを取得する(AutoCAD 2011)

今日もアンケートでいただいた .NET の質問についてです。

いただいた質問は、文字列の長さを取得する方法は? というものです。
確かに、文字によって文字幅が異なってくるので、文字数を元に求められないですよね。
例えば、同じ 5 文字でも 『iiiii』 と 『WWWWW』 では文字幅違いますし、同じ文字でもフォントによって違いますよね。

ということで、探してみました。
最初は AutoLISP に textbox って関数があるので、それと同じようなものはないかなってことで arxmgd.chm で textbox を探してみたんですが、何も出てきません。
じゃあ、オブジェクトの境界を取得することとかできないかなと思って探してみたら、Entity Properties に GeometricExtents っていうのがありました。

早速それを使ってやってみたらオブジェクトの境界を取得できました。
境界が取得できれば、それから文字列の長さってわかりますよね。(普通は X 方向の値。90°回転しているときは Y 方向の値。それ以外の角度で回転しているときは計算しなきゃいけない。)

試してみた時のコードは、こんな感じです。

'--------------------------------------------------
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim db As Database = doc.Database

Using trans As Transaction = db.TransactionManager.StartTransaction
    Try
        Dim ed As Editor = doc.Editor
        Dim selEntID As ObjectId = ed.GetEntity("オブジェクトを選択").ObjectId
        Dim ent As Entity = trans.GetObject(selEntID, OpenMode.ForRead)

        Dim minX As Double = ent.GeometricExtents.MinPoint.X
        Dim minY As Double = ent.GeometricExtents.MinPoint.Y
        Dim maxX As Double = ent.GeometricExtents.MaxPoint.X
        Dim maxY As Double = ent.GeometricExtents.MaxPoint.Y

        MsgBox("選択したオブジェクトの境界は、X = " & (maxX - minX) & "、Y = " & (maxY - minY) & " です。")

    Catch ex As Autodesk.AutoCAD.Runtime.Exception
        Application.ShowAlertDialog(ex.Message)
    End Try
End Using
'--------------------------------------------------

2010年10月25日月曜日

.NET - DLL ファイルを自動的にロードする(AutoCAD 2011)

先日いただいたアンケートで、.NET を自動的にロードする方法の質問があったので、今日はそれについて書こうと思います。

手動では、NETLOAD コマンドを実行して .DLL ファイルをロードすると、.NET で作成したコマンドが使用できるようになります。

ただし、これだと AutoCAD を起動する度に実行しなきゃいけないので、面倒ですよね。

そこで、勝手にロードされる方法としては、以下のような方法があります。

【acad.lsp でロードする】
acad.lsp に以下の記述を追加しておくと、AutoCAD 起動時に自動的にロードされます。
(例として、AVDO_Point.dll をロードする内容にしています。)

;---------------------------------------------------
(defun-q MYSTARTUP ()
(command "NETLOAD" "AVDO_Point.dll")
(princ)
)

(setq S::STARTUP (append S::STARTUP MYSTARTUP))
;---------------------------------------------------

acad.lsp がどこにあるかは、コマンド ラインに (findfile "acad.lsp") と入力すると確認できます。

そのとき nil が返ってきたら、acad.lsp がないということなので、メモ帳などで新たに作ってサポート ファイルの検索パスにあるどこかのフォルダに置いてください。

【レジストリにデマンドロードの設定を追加する】
これは、.NET で作ったアプリケーションをインストーラでインストールするときに、設定しておくときに使えばいいんじゃないかと思います。

でも、reg ファイルを作って手動でレジストリに追加するってこともできます。

reg ファイルの例としては、以下のとおりです。

;---------------------------------------------------
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R18.1\ACAD-9001:411\Applications\AVDO_Point]
"DESCRIPTION"="ACAD Video"
"LOADER"="C:\\Program Files\\Autodesk\\AutoCAD 2011\\acadvideo\\AVDO_Point.dll"
"LOADCTRLS"=dword:00000002
"MANAGED"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R18.1\ACAD-9001:411\Applications\AVDO_Point\Commands]
"AVDO_DDPTYPE"="AVDO_DDPTYPE"
"AVDO_Point"="AVDO_Point"
"AVDO_POINT_EVENT"="AVDO_POINT_EVENT"
"AVDO_POINT_EVENT_REMOVE"="AVDO_POINT_EVENT_REMOVE"


[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R18.1\ACAD-9001:411\Applications\AVDO_Point\Groups]
"AVDO_Point"="AVDO_Point"
;---------------------------------------------------

"LOADER"="C:\\Program Files\\Autodesk\\AutoCAD 2011\\acadvideo\\AVDO_Point.dll" のとことでロードする .DLL ファイルを指定しています。

"LOADCTRLS"=dword:00000004 のところで、どんなときにロードするかを指定しています。
dword:00000002 は、AutoCAD 起動時にロードするということです。
dword:00000004 は、コマンド実行時にロードするということです。ロードするものがいっぱいあって、AutoCAD の起動が遅い時とかはこれにしておく方がいいかも。
他の値もあるみたいですが、管理人はよく分かっていません。(ARX のときに使う値かも???)

[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R18.1\ACAD-9001:411\Applications\AVDO_Point\Commands] の下にコマンド名を書いています。

[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R18.1\ACAD-9001:411\Applications\AVDO_Point\Groups] の下に、コマンド グループ名を書いています。

2010年10月21日木曜日

.NET - ダイナミック ブロックをコピーする(AutoCAD 2011)

先日アンケートをいただいて、その中にダイナミック ブロックのコピーがうまくいかないという質問をいただきました。

ということで、dblk.dwg という図面を作って試してみました。

dblk.dwg には、上から
        test という名前のダイナミック ブロック
        test2 という名前のダイナミック ブロック
        円
がありますが、clone メソッドでコピーしてみると、下の2つは問題ないのですが、test に関しては、ブロック名が test ではなくて *U4 になってしまいました。

きっとダイナミック ブロックはコピーしなきゃいけない情報が他のオブジェクトよりもあるのでしょう。

そこで、いろいろググッてみました。
なかなかそれらしいのが見つからなかったのですが DeepCloneObjects っていうのでできそうってことをやっと見つけました。

何度か諦めようかと思ったのですが、やっぱりあると信じて辛抱強く探すことが大事だなと再認識です。

ということで、以下のような感じでやってみたら test という名前のダイナミック ブロックもブロック名が変わることなくコピーできました。

'---------------------------------------------------------
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim db As Database = doc.Database
Dim ed As Editor = doc.Editor

Using trans As Transaction = db.TransactionManager.StartTransaction
    Try
        Dim selEntID As ObjectId = Application.DocumentManager.MdiActiveDocument.Editor.GetEntity("オブジェクトを選択").ObjectId
        Dim objIds As ObjectIdCollection = New ObjectIdCollection()
        objIds.Add(selEntID)
        Dim idMap As IdMapping = New IdMapping
        db.DeepCloneObjects(objIds, db.CurrentSpaceId, idMap, False)
        For Each idP As IdPair In idMap
            Dim cpObj As DBObject = trans.GetObject(idP.Value, OpenMode.ForWrite)
            If TypeOf cpObj Is Entity Then
                Dim cpEnt As Entity = cpObj
                cpEnt.ColorIndex = 1

                Dim pt1 As Point3d = New Point3d(0, 0, 0)
                Dim vec1 As Vector3d = pt1.GetVectorTo(New Point3d(110, 0, 0))
                cpEnt.TransformBy(Matrix3d.Displacement(vec1))
            End If
        Next

        trans.Commit()

    Catch ex As Autodesk.AutoCAD.Runtime.Exception
        Application.ShowAlertDialog(ex.Message)

    End Try
End Using
'---------------------------------------------------------


それと、全然別件なのですが、このときたまたま CurrentSpaceId っていうのがあることに気が付きました。
今まで管理人は、現在モデル空間にるかペーパー空間にいるかの判断を以下のように CVPORT を見てやっていました。

'---------------------------------------------------------

If Application.GetSystemVariable("CVPORT") = 1 Then
    btRec = trans.GetObject(bt(BlockTableRecord.PaperSpace), OpenMode.ForWrite)
Else
    btRec = trans.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite)
End If

'---------------------------------------------------------

でも、以下のように CurrentSpaceId を使った方が良さそうですね。

'---------------------------------------------------------

Dim cSpace As BlockTableRecord = trans.GetObject(db.CurrentSpaceId, OpenMode.ForRead)
If cSpace.Name = "*Model_Space" Then
    btRec = trans.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite)
Else
    btRec = trans.GetObject(bt(BlockTableRecord.PaperSpace), OpenMode.ForWrite)
End If

'---------------------------------------------------------

ということで、これからは CurrentSpaceId を使おうかと思います。

2010年9月2日木曜日

.NET - ブロックに含まれる属性の値と位置を取得するサンプル コード(AutoCAD 2011)

.NET でブロックに含まれる属性の値と位置を所得できるか試してみました。

属性値は AttributeReference.TextString Propertyで、位置は AttributeReference.Position Property で取得できました。

位置に関しては、ブロックが入れ子になっているときは、TransformBy Method で Matrix3d を使用してあげるとちゃんとした位置を取得できるようです。

サンプル コードでは、入れ子は 1 階層まで見るようになってます。また、ブロックに尺度が付いている場合は、X と Y が同じ尺度になっている場合にうまく行くようになっています。X と Y の尺度が違う時は、正しい場所を取得してくれないと思います。もし、X と Y の尺度が違う時も対応したいときは、別のマトリックスの指定をする必要があると思います。

サンプル コードは こちら です。
DLL は こちら です。
試してみた図面は こちら です。

2010年9月1日水曜日

.NET(サンプル プログラム) - 「実行中のコマンドのエイリアスを設定」(AutoCAD 2011)

例えば、LINE[線分] コマンドを実行するときに、コマンド ラインに 「L」 と入力してコマンドを実行することができますよね。
慣れていると、リボンのタブを選んでボタンを選んでってするより早いと思います。

この設定は、acad.pgp ってファイルにされているんですが、それを変更する方法としては [管理] タブの [エイリアスを編集] を選択して、手動で acad.pgp を修正したり、Express Tools の ALIASEDIT[Command Alias] コマンドで変更することができます。

でも、ちょっと面倒くさくてなかなかやらないかなっと思ったので、今回は、もっと簡単に設定することができないかと思って、.NET でカスタマイズしてみました。

使い方はこんな感じです。
  1. NETLOAD コマンドで AVDO_SetAlias.dll をロードする。
    ※ acad.lsp で自動的にロードするよう設定しておけば、AutoCAD を起動するたびにロードする必要がないので楽ちんです。
  2. エイリアスを設定したいコマンドを実行。
  3. 割り込みコマンドで AVDO_SetAlias コマンドを実行。
  4. 「コマンドに対するエイリアスを入力:」 に対して設定したいエイリアスを入力。
※ AVDO_SetAlias コマンド自体にエイリアスを設定しておけば、もっと簡単になると思います。
※ 実行中のコマンドに対して設定するので、ダイアログが表示されるコマンドや、REGEN コマンドのようにコマンドを実行するとすぐに終わっちゃって、コマンド実行中にコマンドラインに入力できないコマンドに対して設定することはできません。

その他細々としたことに関しても動画を作りましたので見てみてください。



DLL は こちら、ソースは こちらです。

※ 今回から、Google ドキュメントにファイルおいて、上の 「こちら」 にリンクしてます。んー、Google ドキュメントって便利だねー。

2010年8月20日金曜日

.NET - ブロックに含まれる属性の値を変更するサンプル コード(AutoCAD 2011)

.NET でブロックに含まれる属性の値を変更できるか試してみました。

試してみたところ、手順としては以下のような感じでできました。
  1. 図面中のブロック参照を抽出
  2. 該当する名前のブロック参照に対して AttributeCollection を称して属性を抽出
  3. 属性の値(TextString)を変更
AVDO_UpdateAttribute.dll は こちら、ソースは こちら、試しに使用する test.dwg は こちら にあります。

試し方は以下のとおりです。
  1. NETLOAD コマンドで AVDO_UpdateAttribute.dll をロードします。
  2. test.dwg を開きます。(属性付きのブロック参照 『表題欄』 が表示されます。)
  3. AVDO_UpdateAttribute コマンドを実行します。
  4. 表示された [属性編集] ダイアログで、新しい値を入力して [OK] ボタンをクリックします。
これで、ブロック参照 『表題欄』 に含まれる属性の値が変更されます。

2010年8月19日木曜日

.NET(サンプル プログラム) - 「AutoCAD LT の グループ ツールバーと同様の動作にする」(AutoCAD 2011)

AutoCAD LT のグループ コマンドは AutoCAD のとは違うということはなんとなく知ってはいたんですが、今回いろいろ触ってみました。確かに AutoCAD LT の方が簡単にグループを作ったり分解したりできるんですね。

AutoCAD LT には、PKFSTGROUP や PKUNGROUP っていう AutoCAD には無いコマンドがあります。
これらのコマンドは、PICKFIRST に対応してるので、簡単にグループを作ったり分解できます。(AutoCAD の GROUP コマンドは PICKFIRST に対応してないようです。)

また、ツイッターで 『AutoCAD LTの「グループ」ツールバーをAutoCADでも使えるようにしたい』 っていうリクエストもいただいたので、PKFSTGROUP や PKUNGROUP のようなコマンドを AutoCAD でも使えるように .NET で作ってみました。
(他にも何かリクエストがあれば、アンケート などからお知らせいただければ、できる限りブログに情報をアップしたいと思ってます。)

AVDO_PKFSTGROUP.dll が こちら で、ソースが こちら です。

AutoCAD LT の PKFSTGROUP コマンドに対応するのが AVDO_PKFSTGROUP コマンドです。
AutoCAD LT の PKUNGROUP コマンドに対応するのが AVDO_PKUNGROUP コマンドです。

また、ツールバーの作成例も含んだ動画を作りましたので、見てみてください。



ツールバーのマクロは以下のとおりです。
【グループ】
^C^C_AVDO_PKFSTGROUP

【グループを解除】
^C^C_AVDO_PKUNGROUP

【グループ管理...】
^C^C_GROUP

【グループ選択オン/オフ】
'_setvar;pickstyle;$M=$(if,$(eq,$(getvar,pickstyle),0),1,$(if,$(eq,$(getvar,pickstyle),1),0,$(if,$(eq,$(getvar,pickstyle),2),3,2))) 

2010年8月13日金曜日

.NET(サンプル プログラム) - 「点オブジェクトと似た動作をするブロックの作成」(AutoCAD 2011)

久しぶりに .NET の勉強のため、サンプル プログラムを作ってみました。

しばらくやらないと、せっかく勉強したこともなかなか思い出せなくてダメですね。

DDPTYPE[点スタイル管理 D] コマンドで点オブジェクトの表示スタイルを変更できますが、表示スタイルは AutoCAD が用意したものしか使えないので、ブロックを点オブジェクトと似た動作をするようにしてみました。

ただし、ブロックなので、レイアウトで複数のビューポートがあるときは、点オブジェクトと違った動きになります。

プログラムの内容としては、今まで勉強してきた以下の機能を使ってみました。
ディクショナリの登録
ブロックに Xrecord を追加
イベント
などなど
作ったコマンドはこんな感じです。
AVDO_DDPTYPE(または AVD)コマンド : [点スタイル管理(AVDO_Point)] ダイアログを表示するコマンド
AVDO_POINT(または AVP)コマンド : ブロックを点として挿入するコマンド
AVDO_POINT_EVENT(または AVPE)コマンド : イベントを登録するコマンド
AVDO_POINT_EVENT_REMOVE(または AVPER)コマンド : イベントの登録を解除するコマンド

動作の詳細は、動画を見てみてください。



DLL は こちら、ソースは こちら にあります。

※ レイアウトでモデル空間をアクティブにしても、ペーパー空間にブロックが作成されてしまう間違いを修正してファイルを置き換えました。(2010/08/14)
※ ロックされた画層に点ブロックがあって再作図すると大きさが変わらない現象も見つけたので修正してファイルを置き換えました。(2010/08/14)

2010年7月11日日曜日

.NET - 「他の図面にバックグラウンドでアクセス」(AutoCAD 2011)

今開いている図面以外の図面に対してバックグラウンドでアクセスできるか試してみました。

ReadDwgFile メソッドで今開いていない図面を読み込むことができるようです。

試しに作成したコードは こちら で、ビルドした DLL は こちら です。

AVDO_CheckLayer コマンドを実行して、図面ファイルを選択すると、選択した図面にあって今開いている図面にない画層があれば、それが表示されます。

動画も作ってみましたので、見てみてください。



これと同じ方法で、2つの図面のシステム変数の比較ができるかもしれません。
そうすれば、特定の図面でのみ発生する現象がシステム変数の影響かどうかを確認しやすくなりそうなので、後日チャレンジしてみたいと思います。

2010年7月6日火曜日

.NET - 「AutoCAD をバックグラウンドで実行する方法見つからず」(AutoCAD 2011)

Twitter でもつぶやいていたんですが、Windows アプリから AutoCAD をバックグラウンドで起動して何か実行することをやろうと思ったのですが、残念ながら方法を見つけることができませんでした。

今回はギブアップだったのですが、一応履歴としてどんなことしたか書いとこうかと思います。



「AutoCAD .NET デベロッパガイド」を参考に、CreateObjec を使ってこんな感じで AutoCAD を起動することができました。
Dim acAppComObj As AcadApplication
acAppComObj = CreateObject("AutoCAD.Application.18.1")

で、起動したあとにこんな感じで NETLOAD コマンドを実行してロードしたあとコマンドを実行します。
acDocComObj.SendCommand("(command " & Chr(34) & "NETLOAD" & Chr(34) & " " & _
Chr(34) & "c:/myapps/mycommands.dll" & Chr(34) & ") ")

acDocComObj.SendCommand("MyCommand ")

実行するコマンドで、最後に図面を保存して AutoCAD を終了するようにしておけば、勝手に作業を初めて勝手に終了するという動作になります。


【できなかったこと】
CreateObject で AutoCAD を起動できたんですが、AutoCAD のウインドウを表示することなくバックグラウンドで動かすことができませんでした。

acAppComObj.Visible = False とすれば非表示になるんですが、一旦 AutoCAD が起動してくるのが見えて、その後非表示という動きになりました。また、その後 SendCommand を実行すると、また AutoCAD のウインドウが表示されてしまいました。


ということで、AutoCAD を起動する方法として探してみたら、こんな方法がありました。
Dim regKey As RegistryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\Autodesk\AutoCAD\R18.1\ACAD-9001:411")
Dim acadLoc As String = DirectCast(regKey.GetValue("Location"), String)

Dim acad As ProcessStartInfo = New ProcessStartInfo(acadLoc & "\acad.exe")
acad.Arguments = "/nologo"
'acad.WindowStyle = ProcessWindowStyle.Hidden

Dim pro As Process
pro = Process.Start(acad)

この後、こんな感じで AutoCAD のインスタンスを取得することができます。
Dim acAppComObj() As AcadApplication
acAppComObj = GetObject(, "AutoCAD.Application.18.1")

でも、これだとプログラムが AutoCAD が起動してくるまで待ってくれないで先に進んでしまうので、ちゃんと動きません。

WaitForInputIdle っていうので待ってくれるかなと思ったんですが、待ってくれませんでした。

そこで、WaitForExit(20000) で 20 秒間待つようにしてみたところ、ちゃんと動くようになりました。
でも、もし AutoCAD が起動するまで 20 秒以上たったらちゃんと動かないし、安全のためもっと長くしても時間がかかってもったいないですよね。

また、もっと大きな問題で、AutoCAD が他に起動されていると、そっちに SendCommand で指定したコマンドが送られてしまうのでおかしなことになってしまいます。

複数 AutoCAD が起動しているときに識別する方法がないかググッてみたのですが、残念ながらその方法を見つけることができませんでした。(VB.NET では無理なのかも???)

ということで、今回はギブアップしてしまいました…残念。

2010年7月2日金曜日

.NET(サンプル プログラム) - 「図面で実行されたコマンドを集計」(AutoCAD 2011)

ディクショナリの使い方を勉強してみました。

ディクショナリを作って、情報を保存して、その情報を取得してといった一通りを試してみました。

どんな動作にしようかと思ったのですが、とりあえず作ってみたコマンドと関数はこんな感じです。
  • AVDO_StartCountCommands : コマンドが開始されたときのイベントを登録し、ディクショナリを追加するコマンド
  • CommandStarted : コマンドが開始されたときに実行する関数
  • AVDO_EndCountCommands : コマンドが開始されたときのイベントの登録を解除するコマンド
  • AVDO_RemoveCountCommandsDictionary : 追加したディクショナリを削除するコマンド
  • AVDO_ShowCommandsCount : 各コマンドの実行回数を表示するコマンド
コードは こちら で、ビルドした DLL は こちらです。

【使い方の例】
  1. NETLOAD コマンドで CountCommands.dll をロード
  2. AVDO_StartCountCommands コマンドを実行
  3. いくつかコマンドを実行した後、AVDO_ShowCommandsCount コマンドを実行すると、どのコマンドを何回実行したか表示されます。
※ AutoCAD が自動的に実行しているコマンドも表示されます。
※ REDRAW、REDRAWALL、QVDRAWING、QVLAYOUT コマンドに対してはうまく動かなかったので、除外しました。

使ってみた動画も作ってみました。

2010年6月25日金曜日

.NET - 「画層の追加と削除」(AutoCAD 2011)

以前、画層の追加を試してみましたが、今回は画層の削除も試してみました。


Erase メソッドで画層は削除できるのですが、削除していい画層かどうかを確認しておかないと大変な事になってしまうので、注意が必要です。

例えば、「テスト」画層があって、その画層に線分などがあるのに、「テスト」画層を消しちゃったら図面がおかしくなっちゃいますよね。


削除していいかどうかは、Purge メソッドで確認することになります。(Purge メソッドを実行すると、削除できないオブジェクトはオブジェクトIDコレクションからなくなるので、それをもとに判断)

試しに作ってみたコードは こちら です。

2010年6月18日金曜日

.NET で気を付けること 「ドキュメントのロックと解除」

今日は、先週の .NET のトレーニングで教えてもらった、ドキュメントのロックと解除について書こうと思います。

モーダル ダイアログのときはいいのですが、モードレス ダイアログを使うときは、ドキュメントをロックしてから作業を行い、最後にロックを解除する必要があるそうです。

モードレス ダイアログがアクティブになると、元のドキュメントが非アクティブになり、そういうときはドキュメントをロックする必要があるそうです。

なので、コンテキスト メニューに自分のメニューを追加して実行する時もドキュメントをロックする必要があります。

それをしないと、複数の図面を操作している時とかにエラーになることがあるようです。(異常終了することも???)

以前、試しにダイアログを作ってみましたが、ロックのことは知りませんでした。
そのときは、コマンドを実行するたびモードレス ダイアログが表示されてしまって、モードレス ダイアログが表示されているかどうか確認する方法を知らなかったので、モーダル ダイアログにしていたので気がつきませんでした。

ということで、モードレス ダイアログやコンテキスト メニューなどを使ってプログラムを作成するときは気を付けましょう。


こんな感じでロックして
    Dim docLock As DocumentLock = Application.DocumentManager.MdiActiveDocument.LockDocument()

作業終了後にこんな感じで解除します。
    docLoc.Dispose()



「.NET デベロッパ ガイド」にもこんな記述がありました。

------------------------------------------------------
ドキュメントのロックとロック解除


オブジェクトの変更要求や AutoCAD へのアクセスは、さまざまな状況で、いくつものアプリケーションから発生します。他の要求との競合を防ぐため、変更を加える前にドキュメントをロックする必要があります。特定の状況でドキュメントをロックできなかった場合、データベースの変更時にロック違反が発生します。次の場合に、ドキュメントのロックが必要になります。

  • アプリケーションでモードレス ダイアログ ボックスから AutoCAD とやり取りする
  • アプリケーションでカレント ドキュメント以外のロード済みドキュメントにアクセスする
  • アプリケーションが COM サーバとして使用されている
  • アプリケーションでコマンドを Session コマンド フラグに登録する

たとえば、現在のドキュメント以外のドキュメントにあるモデル空間またはペーパー空間に図形を追加する場合、ドキュメントをロックする必要があります。ロックするには、ロックする Database オブジェクトの LockDocument メソッドを使用します。LockDocument メソッドが呼び出されると、DocumentLock オブジェクトが返されます。

ロックしたデータベースの変更を終えたら、データベースをロック解除する必要があります。データベースをロック解除するには、DocumentLock オブジェクトの Dispose メソッドを呼び出します。DocumentLock オブジェクトで Using 文を使用することもできます。Using 文が終了するとデータベースがロック解除されます。

注: Session コマンド フラグを使用しないコマンドで作業する場合、変更前に現在のドキュメントのデータベースをロックする必要はありません。
------------------------------------------------------

2010年6月11日金曜日

.NET(サンプル プログラム) - 「寸法を特定の画層に作成する」(AutoCAD 2011)

イベントっていうので、コマンド実行時やコマンド終了時などなどにある処理を実行するっていうことができるみたいです。
なので、早速試してみました。

試しに作ってみたプログラムの動作はこんな感じです。

-------------------------------------------------------------
・ コマンド名は、ACVD_DrawDimOnDimLayer です。
  このコマンドを、図面ごとに一回だけ実行すると、以下のコマンドが実行されたときに自動的に動作します。
        DIMLINEAR
        DIMALIGNED
        DIMANGULAR
        DIMARC
        DIMRADIUS
        DIMDIAMETER
        DIMORDINATE
        DIMJOGGED

・ 「寸法」画層が存在しない場合は、「寸法」画層を作成する。

・ コマンド終了後、現在層が元に戻る。

・ ACVD_DrawDimOnDimLayer_Remove コマンドを実行すると、その図面ではこの動作は行わなくなります。
-------------------------------------------------------------

コードは こちらです。
dll ファイルは こちら です。

2010年6月4日金曜日

.NET の勉強 「表示、フリーズ、ロック の設定を反転する」(AutoCAD 2011)

以前、悩み事 : 画層のフリーズ状態を反転する簡単な方法は?(AutoCAD LT 2011) で画層のフリーズ状態を反転する簡単な方法はないかなぁ ということを書きました。


折角 .NET の勉強を始めたので、そのコマンドを作ってみました。(.NET なので、LT では使えませんが…)


書いたコードはこんな感じです。

        ' 画層の フリーズ や ON/OFF の設定を逆にするコマンド
        <CommandMethod("ACVD_Blog", "_ACVD_ToggleLayer", "ACVD_ToggleLayer", CommandFlags.Modal)> _
        Public Sub ACVD_ToggleLayer()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim db As Database = doc.Database
            Dim ed As Editor = doc.Editor

            Dim trans As Transaction = db.TransactionManager.StartTransaction
            Dim laTbl As LayerTable = trans.GetObject(db.LayerTableId, OpenMode.ForRead)

            Try
                Dim prRes As PromptResult
                Dim pko As PromptKeywordOptions = New PromptKeywordOptions("")
                pko.Message = vbLf & "どの設定を反転しますか?"
                pko.Keywords.Add("On", "On", "表示(O)")
                pko.Keywords.Add("Freeze", "Freeze", "フリーズ(F)")
                pko.Keywords.Add("Lock", "Lock", "ロック(L)")
                pko.AllowNone = False

                prRes = ed.GetKeywords(pko)
                If prRes.Status <> PromptStatus.OK Then
                    Return
                End If

                For Each objId As ObjectId In laTbl
                    Dim laTblRec As LayerTableRecord = trans.GetObject(objId, OpenMode.ForWrite)

                    ' オプションとして「表示(O)」が選択されたとき
                    If prRes.StringResult.ToString = "On" Then
                        If laTblRec.IsOff = True Then
                            'MsgBox(laTblRec.Name & "画層はオフです")
                            laTblRec.IsOff = False
                        Else
                            'MsgBox(laTblRec.Name & "画層はオンです")
                            laTblRec.IsOff = True
                        End If
                    End If

                    ' オプションとして「フリーズ(F)」が選択されたとき
                    If prRes.StringResult.ToString = "Freeze" Then
                        If laTblRec.IsFrozen = True Then
                            laTblRec.IsFrozen = False

                        ' 現在層はフリーズできないので、現在層の場合は処理を行わないように設定
                        ElseIf laTblRec.ObjectId <> db.Clayer Then
                            laTblRec.IsFrozen = True
                        End If
                    End If

                    ' オプションとして「ロック(L)」が選択されたとき
                    If prRes.StringResult.ToString = "Lock" Then
                        If laTblRec.IsLocked = True Then
                            laTblRec.IsLocked = False
                        Else
                            laTblRec.IsLocked = True
                        End If
                    End If
                Next

                trans.Commit()

            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                Application.ShowAlertDialog(ex.ToString)

            Finally
                trans.Dispose()

            End Try

        End Sub


ビルドした ACAD Video Blog.dll は ここ からダウンロードできます。
これを NETLOAD コマンドでロードして、ACVD_ToggleLayer コマンドを実行すると、「どの設定を反転しますか? [表示(O)/フリーズ(F)/ロック(L)]:」 というオプションが表示されるので、オプションを入力すると、その設定が反転されます。

2010年6月3日木曜日

.NET の勉強 「ユーザ入力のプロンプト - GetString と GetPoint」(AutoCAD 2011)

前回は GetKeywords メソッドを使ってみましたが、ユーザ入力のプロンプトとして GetString メソッド や GetPoint メソッド というのもあるようなので、それらも試してみたいと思います。

で、GetString メソッドですが、オンライン ヘルプにあったサンプルはこんな感じです。


< CommandMethod("GetStringFromUser") > _
Public Sub GetStringFromUser()
  Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument

  Dim pStrOpts As PromptStringOptions = New PromptStringOptions(vbLf & _
                                                               "Enter your name: ")
  pStrOpts.AllowSpaces = True
  Dim pStrRes As PromptResult = acDoc.Editor.GetString(pStrOpts)

  Application.ShowAlertDialog("The name entered was: " & _
                              pStrRes.StringResult)
End Sub


これを実行すると、入力した文字がダイアログに表示されるのですが、それだけじゃおもしろくないので、入力した内容の MTEXT を作成するようにしてみました。
また、MTEXT の基点を GetPoint で取得するようにもしてみました。

        <CommandMethod("ACVD_Blog", "_ACVD_CreateMtext", "ACVD_CreateMtext", CommandFlags.Modal)> _
        Public Sub ACVD_CreateMtext()
            Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
            Dim db As Database = acDoc.Database
            Dim ed As Editor = acDoc.Editor
            Dim trans As Transaction = db.TransactionManager.StartTransaction

            Try
                Dim pStrOpts As PromptStringOptions = New PromptStringOptions(vbLf & "文字を入力: ")
                pStrOpts.AllowSpaces = True

                Dim pStrRes As PromptResult = ed.GetString(pStrOpts)
                If pStrRes.Status <> PromptStatus.OK Then
                    Return
                End If

                Dim ppo As PromptPointOptions = New PromptPointOptions(vbLf & "基点を指定: ")
                ppo.AllowNone = False

                Dim pr As PromptPointResult = ed.GetPoint(ppo)
                If pr.Status <> PromptStatus.OK Then
                    Return
                End If


                Dim blkTbl As BlockTable = trans.GetObject(db.BlockTableId, OpenMode.ForRead)
                Dim br As BlockTableRecord = trans.GetObject(blkTbl(BlockTableRecord.ModelSpace), OpenMode.ForWrite)

                Dim mt As MText = New MText()
                mt.Location = pr.Value
                mt.TextHeight = 5.0
                mt.ColorIndex = 1
                mt.Contents = pStrRes.StringResult

                br.AppendEntity(mt)
                trans.AddNewlyCreatedDBObject(mt, True)
                trans.Commit()

            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                Application.ShowAlertDialog(ex.ToString)

            Finally
                trans.Dispose()
            End Try
         End Sub

2010年5月28日金曜日

.NET の勉強 「画層状態を復元するサンプル」(AutoCAD 2011)

なんか最近 .NET が続いてますが、覚え始めでおもしろくて、ちょっとはまってきたかも。
ということで、今日も .NET です。


acad_mdg.chm「AutoCAD .NET デベロッパ ガイド」のサンプルに画層状態を復元するものがあったので試してみました。

サンプルにあったコードはこんな内容です。
<CommandMethod("RestoreLayerState")> _
Public Sub RestoreLayerState()
  '' Get the current document
  Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument

  Dim acLyrStMan As LayerStateManager
  acLyrStMan = acDoc.Database.LayerStateManager

  Dim sLyrStName As String = "ColorLinetype"

  If acLyrStMan.HasLayerState(sLyrStName) = True Then
      acLyrStMan.RestoreLayerState(sLyrStName, _
                                   ObjectId.Null, _
                                   1, _
                                   LayerStateMasks.Color + _
                                   LayerStateMasks.LineType)
  End If
End Sub

で、昨日つくったやつでは、ACVD_LayerState っていう画層状態に保存するようにしたので、ACVD_LayerState があれば、その状態を復元するように変更してみました。

変更後のコードはこんな感じです。

        ' ACVD_LayerState という画層状態が存在すれば、それを復元するサンプル(acad_mdg.chm「AutoCAD .NET デベロッパ ガイド」のサンプルを元に作成)
        <CommandMethod("ACVD_Blog", "_ACVD_RestoreLayerState", "ACVD_RestoreLayerState", CommandFlags.Modal)> _
        Public Sub ACVD_RestoreLayerState()
            '' Get the current document
            Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = acDoc.Editor

            Dim acLyrStMan As LayerStateManager
            acLyrStMan = acDoc.Database.LayerStateManager

            Dim sLyrStName As String = "ACVD_LayerState"

            Try
                If acLyrStMan.HasLayerState(sLyrStName) = True Then

                    ' 本当に画層状態を復元するかどうかの確認を求める
                    Dim pr As PromptResult
                    Dim pko As PromptKeywordOptions = New PromptKeywordOptions("")
                    pko.Message = vbLf & "画層状態を復元しますか?"
                    pko.Keywords.Add("Yes", "Yes", "はい(Y)")
                    pko.Keywords.Add("No", "No", "いいえ(N)")
                    pko.AllowNone = False
                    pr = ed.GetKeywords(pko)
                    If pr.Status <> PromptStatus.OK Then
                        Return
                    End If

                    ' 画層状態の復元
                    If pr.StringResult.ToString = "Yes" Then
                        acLyrStMan.RestoreLayerState(sLyrStName, _
                                                     ObjectId.Null, _
                                                     1, _
                                                     LayerStateMasks.On + _
                                                     LayerStateMasks.Frozen + _
                                                     LayerStateMasks.Locked + _
                                                     LayerStateMasks.Plot + _
                                                     LayerStateMasks.Color + _
                                                     LayerStateMasks.LineType + _
                                                     LayerStateMasks.LineWeight + _
                                                     LayerStateMasks.Transparency + _
                                                     LayerStateMasks.PlotStyle + _
                                                     LayerStateMasks.NewViewport)
                    Else
                        Return
                    End If
                Else
                    Application.ShowAlertDialog("ACVD_LayerState という画層状態が存在しないため、画層状態を復元できません。")
                End If
            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                MsgBox(ex.ToString)
            End Try
        End Sub



間違えてコマンドを実行してしまうとまずいので、「画層状態を復元しますか?」と表示して はい(Y) か いいえ(N) を選ぶように追加してみました。
GetKeywords っていうのをこんな感じかなと思って入れてみたのですが、とりあえずこれで試してみたら、一応ちゃんと動きました。
ビルドした DLL ファイルは こちら です。

NETLOAD コマンドで ACAD Video Blog.dll をロードして、ACVD_RestoreLayerState コマンドを実行すると、ACVD_LayerState という画層状態が存在すれば、それが復元されます。

2010年5月27日木曜日

.NET の勉強 「画層状態を保存するサンプル」(AutoCAD 2011)

acad_mdg.chm「AutoCAD .NET デベロッパ ガイド」のサンプルに画層状態を保存するものがあったので試してみました。

サンプルにあったコードはこんな内容です。
<CommandMethod("RestoreLayerState")> _
Public Sub RestoreLayerState()
'' Get the current document
Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument

Dim acLyrStMan As LayerStateManager
acLyrStMan = acDoc.Database.LayerStateManager

Dim sLyrStName As String = "ColorLinetype"

If acLyrStMan.HasLayerState(sLyrStName) = True Then
acLyrStMan.RestoreLayerState(sLyrStName, _
ObjectId.Null, _
1, _
LayerStateMasks.Color + _
LayerStateMasks.LineType)
End If
End Sub

保存するのは、色と線種だけみたいなので、その他の設定も保存するように変更してみました。

また、既に同じ名前の画層状態があったら、何もしてくれないので、既に同じ名前の画層状態があったら、削除して作りなおすようにしてみました。

それと、Try~Catch も入れてみました。こんな使い方でいいのかなぁ…

変更後のコードはこんな感じです。

        ' 画層状態の保存のサンプル(acad_mdg.chm「AutoCAD .NET デベロッパ ガイド」のサンプルを元に作成)
        <CommandMethod("ACVD_Blog", "_ACVD_CreateLayerState", "ACVD_CreateLayerState", CommandFlags.Modal)> _
        Public Sub ACVD_CreateLayerState()
            '' Get the current document
            Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument

            Dim acLyrStMan As LayerStateManager
            acLyrStMan = acDoc.Database.LayerStateManager

            Dim sLyrStName As String = "ACVD_LayerState"

            Try
                ' もし、既に同じ名前の画層状態があったら削除する
                If acLyrStMan.HasLayerState(sLyrStName) = True Then
                    acLyrStMan.DeleteLayerState(sLyrStName)
                End If

                ' 画層状態を保存する
                acLyrStMan.SaveLayerState(sLyrStName, _
                                          LayerStateMasks.On + _
                                          LayerStateMasks.Frozen + _
                                          LayerStateMasks.Locked + _
                                          LayerStateMasks.Plot + _
                                          LayerStateMasks.Color + _
                                          LayerStateMasks.LineType + _
                                          LayerStateMasks.LineWeight + _
                                          LayerStateMasks.Transparency + _
                                          LayerStateMasks.PlotStyle + _
                                          LayerStateMasks.NewViewport, _
                                          ObjectId.Null)
            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                MsgBox(ex.ToString)
            End Try
          
        End Sub


これで試してみたら、一応ちゃんと動きました。
ビルドした DLL ファイルは こちら です。

NETLOAD コマンドで ACAD Video Blog.dll をロードして、ACVD_CreateLayerState コマンドを実行すると、現在の画層状態が ACVD_LayerState という名前で保存されます。

2010年5月26日水曜日

.NET の勉強 「役立つ動画がオートデスクの Web サイトに…」(AutoCAD 2011)

今まで、オンライン ヘルプのサンプルとかを見ながら .NET の勉強をしていましたが、なんと オートデスクの Web サイトに動画で説明している資料がありました。知らなかった…


ここ にある AutoCAD 2010 .NET Wizard(誤) DevTV (DeveloperTV)(正) がそれです。(間違えてたので修正しました。2010/05/27 管理人)

ここでは、以下の内容が、実際にコードを書きながら説明されています。

  • 簡単なアプリケーションの作成
  • 図形を図面に追加
  • 図形の編集
  • アプリケーションのデータ保存
  • イベント監視
動画を見ながら自分でもコードを書いて試せるので、とても便利です。


オンライン ヘルプのサンプルでは、Using ~ という記述になってるところがここでは、Try ~Catch という記述になってますね。

エラーの処理ができるので、きっとこっちの方がいいのでしょう。
ということで、今後はこっちで行こうかと思います。

2010年5月21日金曜日

AutoCAD 2011 .Net Wizards を使用して .NET プロジェクトを作成(AutoCAD 2011)

今日は、AutoCAD Managed VB Application Wizard っていうのを使用して .NET プロジェクトを作成できるようなので、それを試してみた結果を書こうかと思います。

まずは、Developer Center にある 「AutoCAD 2011 .Net Wizards.zip」 をダウンロードして、それに含まれる AutoCAD 2011 dotNET Wizards.msi を実行してインストールです。

すると、こんなメッセージがでて、ちゃんとインストールできませんでした。

管理者権限で、Windows 7 にログインしてるんですけど。。。

ぶつぶつ言ってもしょうがないので、コマンド プロンプトを 「管理者として実行」 で起動して、そこから AutoCAD 2011 dotNET Wizards.msi を実行してみました。

すると、今度は問題なくインストールが完了しました。


ということで、早速 Visual Studio 2008 を起動です。


[ファイル] - [新しいプロジェクト] で [新しいプロジェクト] ダイアログを表示して、[Vusual Basic] を選択すると、[AutoCAD 2011 plug-in] というテンプレートがでてきたので、これを選んで OK です。

そうすると、[AutoCAD .NET Wizard Configurator] ダイアログが表示されるので、[Spacify the location of the ...] のことろに ObjectARX 2011 の inc フォルダを指定して Ok です。

これで、プロジェクトが開かれるのですが、ここでは既に参照設定に AcDbMgd と AcMgd が追加されているので、参照設定は何もする必要がないみたいです。

で、MyCommands.vb っていうのがあったので、これを見てみると、こんな記述がありました。
<CommandMethod("MyGroup", "MyCommand", "MyCommandLocal", CommandFlags.Modal)> _
Public Sub MyCommand() ' This method can have any name
    ' Put your command code here
End Sub
ここに、自分で作ったコマンドを入れるってことですね。

ということで、まずはコマンドの定義のところを以下のように変更しました。
<CommandMethod("acadvideo", "test0521", "MyCommandLocal", CommandFlags.Modal)> _
Public Sub test0521() ' This method can have any name

また、ソリューション エクスプローラにある myCommands.res をダブルクリックして myCommands.res を表示して、MyCommand を test0521 に変更しました。


そして、「' Put your command code here」の下に以下を記述してみました。

'' Get the current document and database
Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
Dim acCurDb As Database = acDoc.Database

'' Start a transaction
Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()

    '' Open the Block table for read
    Dim acBlkTbl As BlockTable
    acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)

    '' Open the Block table record Model space for write
    Dim acBlkTblRec As BlockTableRecord
    acBlkTblRec = acTrans.GetObject(acBlkTbl(BlockTableRecord.ModelSpace), OpenMode.ForWrite)

    '' Create a line that starts at 5,5 and ends at 12,3    Dim acLine As Line = New Line(New Point3d(5, 5, 0), New Point3d(12, 3, 0))    acLine.SetDatabaseDefaults()

    '' Add the new object to the block table record and the transaction
    acBlkTblRec.AppendEntity(acLine)
    acTrans.AddNewlyCreatedDBObject(acLine, True)

    '' Save the new object to the database
    acTrans.Commit()
End Using

AutoCAD にロードして test0521 コマンドを実行してみたところ、問題なく線分が作成されました。

このテンプレートを使えば、簡単に作れますよってことですね。

2010年5月14日金曜日

.NET の勉強 「画層の作成」(AutoCAD 2011)

金曜日恒例の .NET の勉強です。
今日は、画層を作成してみました。


ヘルプでサンプルを探して、サンプルの余分なところを削除して、こんなコードを書きました。


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

Public Class Class1
    <CommandMethod("CreateLayer")> _
    Public Sub CreateLayer()
        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
        Dim acCurDb As Database = acDoc.Database

        Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()
            Dim acLyrTbl As LayerTable
            acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForRead)

            Dim sLayerName As String = "テスト"

            If acLyrTbl.Has(sLayerName) = False Then
                Dim acLyrTblRec As LayerTableRecord = New LayerTableRecord()

                acLyrTblRec.Name = sLayerName

                acLyrTbl.UpgradeOpen()

                acLyrTbl.Add(acLyrTblRec)
                acTrans.AddNewlyCreatedDBObject(acLyrTblRec, True)
            End If
            acTrans.Commit()
        End Using
    End Sub
End Class


で、実際に実行してみると、めでたく 「テスト」 という名前の画層が作成されました。


また、ついでに色と線種の設定も試してみました。
以下の内容を acLyrTblRec.Name = sLayerName の後に書いたら、うまく色と線種の設定もできました。


acLyrTblRec.Color = Color.FromColorIndex(ColorMethod.ByAci, 1)
acLyrTblRec.LineWeight = LineWeight.LineWeight020


(Imports Autodesk.AutoCAD.Colors も追加しました。)

2010年5月7日金曜日

.NET の勉強 「ダイアログを表示してコマンド実行」(AutoCAD 2011)

金曜日恒例の .NET の勉強をしました。


今までいくつかサンプルを見ながら試してきましたが、そろそろダイアログを出してみたくなってきたので、試してみました。


arxmgd.chm の [Autodesk.AutoCAD.ApplicationServices Namespace] - [Application Class] に ShowModalDialog Method と ShowModelessDialog というのがあるので、きっとこれですね。


まずは、フォームの作成
まずは、Form1.vb (Windows フォーム)を作成して、ボタンをひとつ追加しました。
で、そのボタンをクリックすると、前回の試してみた円を作成して ZOOM を行う、以下の内容が実行されるように記述しました。

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Runtime

Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument

        acDoc.SendStringToExecute("._circle 2,2,0 4 ", True, False, False)
        acDoc.SendStringToExecute("._zoom _extents ", True, False, False)
    End Sub
End Class



ダイアログの表示
あとは、Form1 を表示するように ShowModalDialog Method または ShowModelessDialog を使えばいいみたいです。

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Runtime

Public Class Class1

    <CommandMethod("test")> _
    Public Sub test()
        Dim f As New Form1()
        Application.ShowModelessDialog(f)
    End Sub
End Class



これで test コマンド実行してみると、ちゃんとダイアログが表示され、ボタンをクリックすると円が作成されて ZOOM が実行されました。


ただし、もう一回 test コマンドを実行すると、またダイアログが表示されました。
コマンドを実行するたびにダイアログが表示されてしまいます。


そこで、ダイアログを表示するところを以下のようにしてみたのですが、ダメでした。

If f.Visible = False Then
    Application.ShowModelessDialog(f)
End If


残念ながら現時点では、どうすればダイアログが既に表示されているかどうかを判断できるかは不明です???


モーダル ダイアログにすれば、ダイアログを閉じないと AutoCAD に戻れないので複数ダイアログが表示されることはないので、とりあえず今回は以下のようにモーダル ダイアログにしてみます。

Application.ShowModalDialog(f)

ただしこのとき、モーダル ダイアログにしたのでダイアログを閉じるコードを Form1.vb の最後に記述しておかないと、ダイアログが自動的に閉じてくれません。
そのため、Form1.vb に以下を追加しました。

 Me.Close()

2010年4月30日金曜日

.NET の勉強 「AutoCAD コマンド ラインへのアクセス」(AutoCAD 2011)

金曜日なので、.NET の勉強をしました。


AutoLISP では、(command "._LINE" "0,0" "100,100" "") みたいに、COMMAND 関数を使って、コマンド ラインに入力するのと同じ手順を書くことによってコマンドを実行することができますが、同じようなことを .NET でできないか調べてみました。


すると、サンプルにこんなのがありました。

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

<CommandMethod("SendACommandToAutoCAD")> _

Public Sub SendACommandToAutoCAD()
  Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  '' Draws a circle and zooms to the extents or
  '' limits of the drawing
  acDoc.SendStringToExecute("._circle 2,2,0 4 ", True, False, False)
  acDoc.SendStringToExecute("._zoom _all ", True, False, False)
End Sub



さっそくこれをカット&ペーストして確認してみたところ、CRCLE コマンドと ZOOM コマンドが無事実行されました。


SendStringToExecute ってやつで実行するんですね。


試しに、

acDoc.SendStringToExecute("._line 0,0 100,100  ", True, False, False)

を追記してみたら、問題なく線分を作成することができました。

2010年4月23日金曜日

.NET の勉強 「arxmgd.chm を見ながらオブジェクトの作成」(AutoCAD 2011)

金曜日なので、.NET の勉強です。


先週は、「今後も、どんどん進んでいこうと思います。」 と言っていましたが、ちょっとヘルプの見方とか少しですが分かってきたような気がするので、もう一度復習でオブジェクトの作図についてやってみようと思います。


C:\ObjectARX 2011\docs フォルダにある arxmgd.chm を見ながら、オブジェクトを描くプログラムを作成したいと思います。


まず最初に、こんなコードを書いてみます。

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

Public Class Class1
     <CommandMethod("DrawObjects")> _
    Public Sub DrawObjects()

        '' Get the current document and database
        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
        Dim acCurDb As Database = acDoc.Database

        '' Start a transaction
        Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()

            '' Open the Block table for read
            Dim acBlkTbl As BlockTable
            acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)

            '' Open the Block table record Model space for write
            Dim acBlkTblRec As BlockTableRecord
            acBlkTblRec = acTrans.GetObject(acBlkTbl(BlockTableRecord.ModelSpace), _
                                                  OpenMode.ForWrite)

            ''線分を作成
            Dim acLine As New Line()

            acBlkTblRec.AppendEntity(acLine)
            acTrans.AddNewlyCreatedDBObject(acLine, True)

            '' Save the new object to the database
            acTrans.Commit()
        End Using
    End Sub
End Class


ここで、線分を作成しているのは、Dim acLine As New Line() になります。
arxmgd.chm の
Autodesk.AutoCAD.DatabaseServices Namespace > Line Class > Line Constructor
にある Line.Line() について以下の記述があります。
Default constructor. Initializes the normal vector to (0,0,1), the start and end points to (0,0,0), and the thickness to 0.0. All coordinates are WCS.
ということで、これを実行すると、始点も終点も 0,0 の線分が作成されます。


また、arxmgd.chm の
Autodesk.AutoCAD.DatabaseServices Namespace > Line Class > Line Properties
を見ると、以下のプロパティがあります。
Angle、Delta、EndPoint、Length、Normal、StartPoint、Thickness
でも、Angle、Delta、Length の Conditions は Read-only と書かれているので、線分を作成するときに EndPoint、Normal、StartPoint、Thickness のプロパティを使えるんだと思います。


そこで、以下のように 4 つのプロパティを指定するよう追記してみました。

Dim acLine As Line = NEW Line()
acLine.StartPoint = New Point3d(50, 50, 0)
acLine.EndPoint = New Point3d(100, 100, 0)
acLine.Normal = New Vector3d(1, 0, 0)
acLine.Thickness = 2


すると、始点が 50,50、終点が 100,100、UCS に依存する押し出し方向が 1,0,0、厚さが 2 の線分が作成されました。

※ Point3d や Vector3d は、Autodesk.AutoCAD.Geometry 名前空間になるので、「Imports Autodesk.AutoCAD.Geometry」 という記述も追加しました。


プロパティには、線分以外のオブジェクトに共通のものがありますよね。例えば、色とか線種とか。
それは、きっと
Autodesk.AutoCAD.DatabaseServices Namespace > Entity Class > Entity Properties
にある内容になるんだと思います。


そこで、以下も追加してみました。

acLine.ColorIndex = 1
acLine.Linetype = "continuous"
acLine.LineWeight = &HD3


すると、赤色で、線種が continuous で、線の太さが 2.11 mm になりました。


では、今度は楕円にチャレンジです。
(Autodesk.AutoCAD.DatabaseServices Namespace > Ellipse Class を見ながらやりました。)


線分と同じような感じで、以下のようにやってみましたが、エラーとなって描けませんでした。

Dim acEllipse As Ellipse = New Ellipse()
acEllipse.StartParam = 0
acEllipse.EndParam = 1
acEllipse.Center = New Point3d(10, 10, 0)
acEllipse.RadiusRatio = 0.5
acEllipse.StartAngle = 0
acEllipse.EndAngle = PI

acBlkTblRec.AppendEntity(acEllipse)
acTrans.AddNewlyCreatedDBObject(acEllipse, True)


何ででしょうね?いろいろ試してみましたが、管理人には何故かわかりませんでした。


そこで、以下のように記述を変えて実行したところ、楕円も作成できました。

Dim acEllipse As Ellipse = New Ellipse(New Point3d(10, 10, 0), Vector3d.ZAxis, New Vector3d(1, 0, 0), 0.5, 0, PI)
acEllipse.StartParam = 0
acEllipse.EndParam = 1
acEllipse.Center = New Point3d(10, 10, 0)
acEllipse.RadiusRatio = 0.5
acEllipse.StartAngle = 0
acEllipse.EndAngle = PI

acBlkTblRec.AppendEntity(acEllipse)
acTrans.AddNewlyCreatedDBObject(acEllipse, True)


※ PI を認識するために、「Imports System.Math」 も追記しました。


以前よりも理解しながらオブジェクトを作れたような気がします。
次回は、今度こそ、先に進んで新しいことにチャレンジしたいと思います。

2010年4月16日金曜日

.NET の勉強 「オブジェクトの削除、コピー、移動」(AutoCAD 2011)

先週から、金曜日は .NET の勉強をしようかということにしたので、今日はその続きです。

前回は、オブジェクトを作成するプログラムの勉強をしたので、今回は、オブジェクトの削除やコピーや移動などを行うサンプルを探してみました。


そこで、こんなサンプルを見つけて、とりあえず実行してみました。

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

<CommandMethod("EraseObject")> _
Public Sub EraseObject()

    '' Get the current document and database
    Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
    Dim acCurDb As Database = acDoc.Database

    '' Start a transaction
    Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()

        '' Open the Block table for read
        Dim acBlkTbl As BlockTable
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)

        '' Open the Block table record Model space for write
        Dim acBlkTblRec As BlockTableRecord
        acBlkTblRec = acTrans.GetObject(acBlkTbl(BlockTableRecord.ModelSpace), _
                                                            OpenMode.ForWrite)

        '' Create a lightweight polyline
        Dim acPoly As Polyline = New Polyline()
        acPoly.SetDatabaseDefaults()
        acPoly.AddVertexAt(0, New Point2d(2, 4), 0, 0, 0)
        acPoly.AddVertexAt(1, New Point2d(4, 2), 0, 0, 0)
        acPoly.AddVertexAt(2, New Point2d(6, 4), 0, 0, 0)

        '' Add the new object to the block table record and the transaction
        acBlkTblRec.AppendEntity(acPoly)
        acTrans.AddNewlyCreatedDBObject(acPoly, True)

        '' Update the display and display an alert message
        acDoc.Editor.Regen()
        Application.ShowAlertDialog("Erase the newly added polyline.")

        '' Erase the polyline from the drawing
        acPoly.Erase(True)

        '' Save the new object to the database
        acTrans.Commit()

    End Using
End Sub



このプログラムは、ポリラインをひとつ作成して、それを削除するようです。


ポリラインの作成に関しては、前回の線分、円、MTEXT の作成と同じように、オブジェクトを宣言したあと、それぞれのプロパティを指定しているようです。(「'' Create a lightweight polyline」 以下の 5 行 および 「'' Add the new object to the block table record and the transaction」 以下の 2 行。)


で、削除しているのは、acPoly.Erase(True) ですね。
意外と簡単ですね。


他のコピーを行うサンプルを見てみたところ、Clone っていうのでコピーするみたいです。
なので、acPoly.Erase(True) の代わりに以下を記述したところ、無事コピーされました。

Dim acPolyClone As Polyline = acPoly.Clone()

acBlkTblRec.AppendEntity(acPolyClone)
acTrans.AddNewlyCreatedDBObject(acPolyClone, True)



ただし、まったく同じ場所にコピーされるので、もとのポリラインと重なっています。
他の場所にコピーしたいときは、どうするんだろう???移動と組み合わせるのかな???


そこで、移動を行うサンプルを見てみたところ、移動する場所を示すマトリックスを作ってから移動するようです。
そこで、以下のように記述を変更して実行してみたところ、無事に コピー+移動 を行うことができました。(基点が 0,0 で、目的点が 2,0)

Dim acPolyClone As Polyline = acPoly.Clone()

Dim acPt3d As Point3d = New Point3d(0, 0, 0)
Dim acVec3d As Vector3d = acPt3d.GetVectorTo(New Point3d(2, 0, 0))
acPolyClone.TransformBy(Matrix3d.Displacement(acVec3d))

acBlkTblRec.AppendEntity(acPolyClone)
acTrans.AddNewlyCreatedDBObject(acPolyClone, True)



ここで、ちょっと疑問に思ったのが、この例ではプログラムで作成したオブジェクトを移動しましたが、元からあるオブジェクトを選択して、移動したいときはどうなるんでしょう???
AutoLISP だと、entsel とか使うやつです。


サンプルを探してみたところ、きっと GetSelection ってやつですね。
そこで、前述の代わりに、以下を記述してみたところ、オブジェクトの選択を求められて、それを移動することができました。

※ このとき、「Imports Autodesk.AutoCAD.EditorInput」 も追加しました。


'' Request for objects to be selected in the drawing area
Dim acSSPrompt As PromptSelectionResult = acDoc.Editor.GetSelection()

'' If the prompt status is OK, objects were selected
If acSSPrompt.Status = PromptStatus.OK Then
    Dim acSSet As SelectionSet = acSSPrompt.Value

    '' Step through the objects in the selection set        
    For Each acSSObj As SelectedObject In acSSet

        '' Check to make sure a valid SelectedObject object was returned            
        If Not IsDBNull(acSSObj) Then
            '' Open the selected object for write                
            Dim acEnt As Entity = acTrans.GetObject(acSSObj.ObjectId, OpenMode.ForWrite)
            If Not IsDBNull(acEnt) Then
                Dim acClone As Entity = acEnt.Clone()
                Dim acPt3d As Point3d = New Point3d(0, 0, 0)
                Dim acVec3d As Vector3d = acPt3d.GetVectorTo(New Point3d(2, 0, 0))

                acClone.TransformBy(Matrix3d.Displacement(acVec3d))
                acBlkTblRec.AppendEntity(acClone)
                acTrans.AddNewlyCreatedDBObject(acClone, True)
            End If
        End If
    Next
End If



まだちゃんと理解できたわけではありませんが、とりあえず、オブジェクトの作成、削除、コピー、移動を実行するプログラムは作れるようになりました。


今後も、どんどん進んでいこうと思います。
きっとそのうち、自然と理解できるるようになるだろうと期待してます。

2010年4月9日金曜日

.NET の勉強 「線分、円、MTEXT の作成」(AutoCAD 2011)

今日は、ながーらくサボっていた .NET の勉強を、やっと再開したので、そこのとについてです。


今回は、何か図形を描いてみたいと思って、サンプル ファイルを探してみました。
まずは、基本からなので、線分を作成する以下のサンプル見つけて、それをやってみました。
(プロジェクトの作成は、以前書いた手順で行いました。)
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry

<CommandMethod("AddLine")> _
Public Sub AddLine()

    '' Get the current document and database
    Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
    Dim acCurDb As Database = acDoc.Database

    '' Start a transaction
    Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()

        '' Open the Block table for read
        Dim acBlkTbl As BlockTable
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)

        '' Open the Block table record Model space for write
        Dim acBlkTblRec As BlockTableRecord
        acBlkTblRec = acTrans.GetObject(acBlkTbl(BlockTableRecord.ModelSpace), _
                                              OpenMode.ForWrite)

        '' Create a line that starts at 5,5 and ends at 12,3
        Dim acLine As Line = New Line(New Point3d(5, 5, 0), _
                                            New Point3d(12, 3, 0))
        acLine.SetDatabaseDefaults()

        '' Add the new object to the block table record and the transaction
        acBlkTblRec.AppendEntity(acLine)
        acTrans.AddNewlyCreatedDBObject(acLine, True)

        '' Save the new object to the database
        acTrans.Commit()
    End Using
End Sub
線分1本描くのにいろいろやらなきゃいけないんですね。
確かに 5,5 から 12,3 の線分が描けましたが、何やってるんでしょう???
管理人には難しすぎて、ほとんど理解不能です。。。


オンライン ヘルプに、こんな絵が載ってました。
サンプルでは、Document、Database、Block table、Block table record の順番に開いているのは、この絵の順番なんだろうか???きっとそうだ。そう思おう。


ということで、あんまり難しいことを理解しようとすると、何も前に進まないので、「'' Create a line that starts at 5,5 and ends at 12,3」のところの作図部分だけをいろいろいじってみることにしました。


どういじったかというと、試しに、もうひとつの線分と 円と MTEXT を描くために、こんな内容を追加してみました。
Dim L1 As Line = New Line()
L1.SetDatabaseDefaults()
L1.StartPoint = New Point3d(0, 0, 0)
L1.EndPoint = New Point3d(100, 100, 0)

acBlkTblRec.AppendEntity(L1)
acTrans.AddNewlyCreatedDBObject(L1, True)


Dim c1 As Circle = New Circle()
c1.SetDatabaseDefaults()
c1.Center = New Point3d(50, 50, 0)
c1.Radius = 20
c1.ColorIndex = 1
c1.Normal = Vector3d.ZAxis

acBlkTblRec.AppendEntity(c1)
acTrans.AddNewlyCreatedDBObject(c1, True)


Dim mt1 As MText = New MText()
mt1.SetDatabaseDefaults()
mt1.Location = New Point3d(50, 50, 0)
mt1.Width = 100.0
mt1.TextHeight = 10.0
mt1.Contents = "てすとです。"

acBlkTblRec.AppendEntity(mt1)
acTrans.AddNewlyCreatedDBObject(mt1, True)
これで、実行してみたところ、無事 線分、円、MTEXT を作成することができました。


各オブジェクトを宣言したあと、それぞれのプロパティを指定してあげれば、オブジェクトを作成できるようです。

  • 線分の場合は、L1 を宣言して、L1 の始点、終点 を指定しました。
  • 円の場合は、c1 を宣言して、c1 の中心点、半径、色、法線方向 を指定しました。
  • MTEXT の場合は、mt1 を宣言して、文字の位置、幅、文字高さ、文字 を指定しました。


なんとなく、コツがつかめたような感じがします。他のオブジェクトもこんな感じで描けると思います。
意味が分からないところがありますが、とりあえず図形は描けるようになりました。


まだ勉強を始めたばかりなので、分からないところばかりにこだわらずに、これからもどんどん次に進んでいこうかと思います。

※ これから、毎週金曜日は .NET の日にしようかなぁー、、、と思ってたりします。