AutoCAD JavaScript API の使い方 ~ その 1 に続けて AutoCAD JavaScript API をご紹介していきます。まずは、AutoCAD らしく、新しいコマンドを作成してみましょう。http://www.autocadws.com/jsapi/v1/docs/index.html でリファレンス マニュアル(英語)を見てみると、Acad 名前空間の Editor に addCommand() という関数が用意されています。この関数は、ちょうど ObjectARX や .NET API と同じように、コマンド グループと共にグローバルコマンド名とローカルコマンド名、また、コマンドが入力された際に呼び出されて、実際のコマンドの振る舞いを実装するコールバック関数を指定するようになっています。この addCommand() を利用して、次のように HELLO コマンドを定義して MyCommand.js というファイル名で保存したとします。 HELLO コマンドを入力すると hello() 関数が呼び出されて、Alert() 関数が “Hello World !” の内容を持つ警告ダイアログを表示する、いった具合の実装です。
function hello() {
alert("Hello World !");
}
Acad.Editor.addCommand(
"MyGroup",
"HELLO",
"HELLO",
Acad.CommandFlag.MODAL,
hello
);
このコマンドを AutoCAD で実行するには、AutoCAD 2014 から登場した WEBLOAD コマンド を利用します。ローカルに保存した MyCommand.js ファイルを指定してロードしようとすると、セキュリティ警告ダイアログが表示されます。(要 URL エンコード)

この警告ダイアログは、AutoCAD 2014 の新機能 ~ その 3 で触れたセキュア ロードメカニズムによるものです。この警告ダイアログの表示を抑止するには、システム変数 TRUSTEDDMAINS にロード先のドメインを指定する必要があります。なお、この値は、OPTIONS コマンドで表示される [オプション] ダイアログの [ファイル] タブで指定可能な TRUSTEDPATHS とは異なり、ユーザ インタフェースを使って指定をすることが出来ない点に注意してください。もし、TRUSTEDDOMAINS にローカル コンピュータで開発中の JavaScript コードのパスを指定する際には、”C:\Users\Toshiaki Isezaki\Desktop” のような形ではなく、”file:///C:/Users/Toshiaki%20 Isezaki/Desktop” のような URL 形式で指定してください。
それでは、警告ダイアログの[ロード] ボタンでロードしてみます。この状態で、HELLO コマンドを実行することができるはずです。

コマンドとシステム変数 でもご紹介しましたが、コマンド定義時にコマンド フラグを指定すれば、アプリケーション実行コンテキストで動作するコマンドを定義することも可能です。もちろん、AutoCAD の標準コマンドを呼び出すこともできます。
JavaScript なのでクラスと呼ぶと語弊がありますが、リファレンス上で Editor.XXX を見てみると、ObjectARX の AcEd クラス、.NET API の Editor クラスに相当する機能を持つ関数が用意されていることがわかると思います。
ここで紹介した HELLO コマンドを実装、ZIP 圧縮した MyCommand.js をこのブログ ポストにアップロードしておきます。
WEBLOAD コマンドで mycommand.js をロードしてから、HELLO コマンドを実行してみてください。
次にオブジェクトを選択してズームする処理を実装を見てみましょう。AutoCAD JavaScript API でも、その他の AutoCAD API と比較して、ある程度、類推可能な名前の関数が多数用意されていることがわかります。ここでは、もちろん、オブジェクトの選択に getEntity() 関数を利用することになります。また、選択したオブジェクトの識別子となるオブジェクト ID の取得など、その後処理はコールバック関数を利用します。
コールバック関数の指定には、他の多くの Editor 関数と同じように Promise Pattern と呼ばれる方法を使用します。具体的には、Acad.Editor.実行関数名.then(成功時のコールバック関数名, 失敗時のコールバック関数名) の形で指定します。次の例では、zoomEntity() 関数内で使われている getEntity() 関数が、Promise Pattern で 2 つのコールバック関数を関連付けていることがわかります。オブジェクトの選択が終了した場合には、OnComplete() が、失敗した場合には OnError() が呼び出されることになります。いずれの関数も、引数に JSON が渡されていて、内容を JSON.parse() でオブジェクト化することで getEntity() の戻り値である PromptEntityResult オブジェクトを取り出しています。PromptEntityOptions や PromptEntityResult など、AutoCAD .NET API をご存じの方にはお馴染みの動作をします。
function zoomEntity() {
try {
var peo = new Acad.PromptEntityOptions();
peo.setMessageAndKeywords("\nSelect an entity", "");
peo.allowObjectOnLockedLayer = true;
Acad.Editor.getEntity(peo).then(onComplete, onError);
}
catch(e) {
write(e.message);
}
}
function onComplete(jsonPromptResult) {
try {
var resultObj = JSON.parse(jsonPromptResult);
if (resultObj && resultObj.status == 5100) {
var entity = new Acad.DBEntity(resultObj.objectId);
var ext = entity.getExtents();
Acad.Editor.CurrentViewport.zoomExtents(
ext.minPoint3d, ext.maxPoint3d, true);
}
}
catch(e) {
write(e.message);
}
}
function onError(jsonPromptResult) {
write("\nProblem encountered.");
}
Acad.Editor.addCommand(
"MyGroup",
"MYZOOM",
"MYZOOM",
Acad.CommandFlag.TRANSPARENT,
zoomEntity
);
この例は、選択したオブジェクトの境界ボックスとなる対角座標を取得して、その範囲にアニメーション付きでズームします。このプログラムを改修して選択したオブジェクトの色を変更するなど、簡単な編集を想定される方もいるでしょう。ところが、この他の関数は、まだ Acad.DBEntity には実装されていません。これが、JavaScript API が、AutoCAD 2014 で初めて提供開始されたばかりであることの査証になっています。もちろん、今後のバージョンで他の関数を拡張していく予定です。現段階では、ObjectARX のacjsDefun() 関数や、.NET API のDefunJS メソッドで、JavaScript API にない機能をエクスポーズすることも出来るようになっています。
先に紹介した MYZOOM コマンドを実装した MyZoom.js をこのブログ ポストにアップロードしておきます。
WEBLOAD コマンドで myzoom.js をロードしてから、MYZOOM コマンドを実行してみてください。
次に、ユーザ入力が連続するような処理を考えてみます。例えば、中心点と半径を入力して円を作図する MYCIRCLE コマンドを考えてみます。この場合、下記の myCircle() 関数の内容のように、実行される順番にプログラムしていくのが、いままでの AutoCAD API の一般的な考え方かと思います。
var m_rad = 0.0;
var m_cpt = new Acad.Point3d(0.0, 0.0, 0.0);
unction pointToString(pt) {
var ret = pt.x.toString() + "," +
pt.y.toString() + "," +
pt.z.toString();
return ret;
}
function doubleToString(value) {
return value.toString();
}
function onCompleteCpt(jsonPromptResult) {
try {
var resultObj = JSON.parse(jsonPromptResult);
if (resultObj && resultObj.status == 5100) {
m_cpt = resultObj.point;
}
}
catch(ex) {
write(ex.message);
}
}
function onCompleteRad(jsonPromptResult) {
try {
var resultObj = JSON.parse(jsonPromptResult);
if (resultObj && resultObj.status == 5100) {
m_rad = resultObj.value;
}
}
catch(ex) {
write(ex.message);
}
}
function onError(jsonPromptResult) {
write("\nエラーが発生");
}
function myCircle() {
try {
var ppo = new Acad.PromptPointOptions();
ppo.useBasePoint = false;
ppo.allowNone = false;
ppo.setMessageAndKeywords("\n中心点を入力", "");
Acad.Editor.getPoint(ppo).then(onCompleteCpt, onError);
}
catch(ex) {
write(ex.message);
}
try {
var pdo = new Acad.PromptDistanceOptions("\n半径を入力");
pdo.useBasePoint = true;
pdo.allowNone = false;
pdo.basePoint = new Acad.Point3d(m_cpt.x, m_cpt.y, m_cpt.z);
Acad.Editor.getDistance(pdo).then(onCompleteRad, onError);
}
catch(ex) {
write(ex.message);
}
Acad.Editor.executeCommand("CIRCLE",
pointToString(m_cpt),
doubleToString(m_rad));
}
Acad.Editor.addCommand(
"MyGroup",
"MYCIRCLE",
"MYCIRCLE",
Acad.CommandFlag.TRANSPARENT,
myCircle
);
紹介した MYCIRCLE コマンドを実装した MyCircle.js をこのブログ ポストにアップロードしておきます。
WEBLOAD コマンドで mycircle.js を AutoCAD 2014 にロードしてから、MYCIRCLE コマンドを実行してみてください。
このプログラムを実行するときには、当然、getPoint() → getDistance() の順番でユーザ入力を待って値を取得し、 円が作図されることを期待します。ところが、実際には、コマンドはユーザの入力を待たずに勝手に終了してしまいます。また、日本語で記入したプロンプト メッセージが文字化けしていることもわかります。

最初に、文字化けの問題から解決していきましょう。最初にご紹介した MYZOOM コマンドでは、表示するメッセージをすべて英語にしていたので気が付かないのですが、JavaScript を記述した .js ファイルは、ASCII テキスト形式ではなく、URF-8 形式で保存する必要があります。特に、日本語を表示するような場面では、必ず、UTF-8 形式で保存するようにしてください。
次に、ユーザ入力を待たない問題の解決です。先のご紹介した Promise Pattern は、JavaScript API で一般的な非同期プログラミングの手法です。複数のWeb ブラウザで表示された異なるコンテンツに対して、インターネットを介して情報を送信した場合、必ずしも送信した順番に結果が表示されるわけではない経験をお持ちと思います。応答速度がネットワークのトラフィック量やサーバーの負荷などに左右されるのはよくある話ですし、インターネットでは一般的な問題です。このインターネットを前提とした JavaScript では、必ずしも同期的な処理が期待できませn。そこで、このような非同期処理に対応する必要あるため、Promise Pattern のような非同期に対応する仕組みが導入されています。いわゆる非同期プログラミングです。これが故に、MYCIRCLE コマンドユーザ入力を待たずに、勝手に終了してしまった訳です。
AutoCAD JavaScript API 以外の AutoCAD API のように、同期させた入力を用いて、作図をおこないたい場面は、コールバック関数内で次のアクションを記述することで、期待した振る舞いを得ることができます。
var m_rad = 0.0;var m_cpt = new Acad.Point3d(0.0, 0.0, 0.0);function pointToString(pt) { var ret = pt.x.toString() + “,” + pt.y.toString() + “,” + pt.z.toString(); return ret;}function doubleToString(value) { return value.toString();}function myCircle() { try { var ppo = new Acad.PromptPointOptions(); ppo.useBasePoint = false; ppo.allowNone = false; ppo.setMessageAndKeywords(“\n中心点を入力”, “”); Acad.Editor.getPoint(ppo).then(onCompleteCpt, onError); } catch(ex) { write(ex.message); }}function onCompleteCpt(jsonPromptResult) { try { var resultObj = JSON.parse(jsonPromptResult); if (resultObj && resultObj.status == 5100) { try { m_cpt = resultObj.value; var pdo = new Acad.PromptDistanceOptions(“\n半径を入力”); pdo.useBasePoint = true; pdo.allowNone = false; pdo.basePoint = new Acad.Point3d(m_cpt.x, m_cpt.y, m_cpt.z); Acad.Editor.getDistance(pdo).then(onCompleteRad, onError); } catch(ex) { write(ex.message); } } } catch(ex) { write(ex.message); }}function onCompleteRad(jsonPromptResult) { try { var resultObj = JSON.parse(jsonPromptResult); if (resultObj && resultObj.status == 5100) { m_rad = resultObj.value; Acad.Editor.executeCommand("CIRCLE", pointToString(m_cpt), doubleToString(m_rad)); } } catch(ex) { write(ex.message); }}function onError(jsonPromptResult) { write(“\nエラーが発生”);}Acad.Editor.addCommand( “MyGroup”, “MYCIRCLE”, “MYCIRCLE”, Acad.CommandFlag.TRANSPARENT, myCircle);
このコードを UTF-8 形式で保存後に WEBLOAD コマンドでロードして、MYCIRCLE コマンドを実行すると、中心点からラバーバンドを表示しながら半径を指定して、CIRCLE コマンドで円を作図することができるはずです。CIRCLE コマンドを実行する Acad.Editor.executeCommand() 関数が、すべて文字列の引数だけを要求する点も他の AutoCAD API と異なる点です。

AutoCAD JavaScript API は、やはり、HTML に埋め込んで、Web コンテンツをユーザ インタフェースとして利用したほうが把握しやすいように思います。
少し時間が空いてしまいますが、「AutoCAD JavaScript API の使い方 ~ その 3」では、AutoCAD JavaScript API を HTML と共に利用する方法をご紹介します。
By Toshiaki Isezaki

You must be logged in to post a comment.