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 を使おうかと思います。