development of

AutoCADやODASDK(旧Teigha)について

【IJCAD】 .NET API 対応状況

これまで確認した IJCAD の.NET APIの対応状況のリストです。

IJCADで .NET APIを使用したカスタマイズをする時の参考にしてもらえればいいと思います。

 

名前空間 ドキュメント
ApplicationServices IJCAD .NET API 対応状況(ApplicationServices) - Google スプレッドシート
Colors IJCAD .NET API 対応状況(Colors) - Google スプレッドシート
DatabaseServices ① IJCAD .NET API 対応状況(DatabaseServices)① - Google スプレッドシート
DatabaseServices ② IJCAD .NET API 対応状況(DatabaseServices)② - Google スプレッドシート
EditorInput IJCAD .NET API 対応状況(EditorInput) - Google スプレッドシート
Geometory IJCAD .NET API 対応状況(Geometory) - Google スプレッドシート
GraphicsInterface IJCAD .NET API 対応状況(GraphicsInterface) - Google スプレッドシート
Internal IJCAD .NET API 対応状況(Internal) - Google スプレッドシート
LayerManager IJCAD .NET API 対応状況(LayerManager) - Google スプレッドシート
PlottingServices IJCAD .NET API 対応状況(PlottingServices) - Google スプレッドシート
Publishing IJCAD .NET API 対応状況(Publishing) - Google スプレッドシート
Runtime IJCAD .NET API 対応状況(Runtime) - Google スプレッドシート
Windows IJCAD .NET API 対応状況(Windows) - Google スプレッドシート

 

※すべてのクラスをチェックしているわけではありません。

※複数のテストパターンを用意していませんので、確認できていない不具合が存在している可能性もあります。

【AutoCAD Mechanical】全ての部品表を取得する

全ての部品表を取得するには

図面内に存在する全ての部品表を取得するには、AcmBOMManager::getAllBomTables メソッドを使用します。
このメソッドを使用すると、図面内のすべての部品表のオブジェクトIDを取得できます。

サンプルコード

void GetAllBom()
{
    AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase();

    AcDbObjectIdArray bomIds;

    // 全ての部品表のObjectIdを取得
    acmBomMgr->getAllBomTables(bomIds);

    for each (AcDbObjectId bomId in bomIds)
    {
        CMapStringToString valueMap;
        CString bomName;

        // 部品表のデータを取得
        acmBomMgr->getBomData(bomId, bomName, valueMap);
        acutPrintf(_T("\n部品表 [%s] の対象は、"), (LPCTSTR)bomName);

        bool isStructuralyExpanded;
        bool isUsingAssmPath;
        CString structSeparator;
        AcDbObjectIdArray assocLayoutIds;
        AcDbObjectId targetId;

        // 部品表のプロパティを取得
        acmBomMgr->getBomProperties(
            bomId,
            isStructuralyExpanded,
            isUsingAssmPath,
            structSeparator,
            assocLayoutIds,
            targetId);

        AcDbObject *obj;

        // 部品表の対象オブジェクトを取得
        acdbOpenAcDbObject(obj, targetId, AcDb::kForRead);

        // 対象オブジェクトが何なのか確認
        if (obj->isKindOf(AcDbBlockReference::desc()))
        {
            // ブロック参照の場合

            AcDbBlockReference *insert = AcDbBlockReference::cast(obj);
            AcDbObjectId blockId = insert->blockTableRecord();
            AcDbBlockTableRecord *block;
            acdbOpenObject(block, blockId, AcDb::kForRead);
            ACHAR *name;
            block->getName(name);
            acutPrintf(_T("図面枠 [%s] です。"), name);
            block->close();
        }
        else if (obj->isKindOf(AcDbBlockTableRecord::desc()))
        {
            // ブロックテーブルレコードの場合

            AcDbBlockTableRecord *block = AcDbBlockTableRecord::cast(obj);
            ACHAR *name;
            block->getName(name);
            if (acdbSymUtil()->isBlockModelSpaceName(name))
            {
                acutPrintf(_T("図面全体です。"));
            }
            else
            {
                acutPrintf(_T("不明のオブジェクトです。"));
            }
        }
        else
        {
            // その他のオブジェクトの場合

            CString cmpdef = _T("ACAMCOMPDEF");
            // オブジェクトのDXF名を確認
            if (cmpdef.Compare(obj->isA()->dxfName()) == 0)
            {
                // DXF名がコンポーネント定義の場合はストラクチャと判断しています
                acutPrintf(_T("ストラクチャです。"));
            }
            else
            {
                acutPrintf(_T("不明のオブジェクトです。"));
            }
        }
        obj->close();
    }
}

※サンプルコードの補足

サンプルコードでは図面全体の部品表、図面枠の部品表、ストラクチャの部品表かどうかをチェックしていて、ブロック参照を対象としている部品表は図面枠の部品表、モデル空間のブロックテーブルレコードを対象としている部品表は図面全体の部品表、その他のオブジェクトの中で "ACAMCOMPDEF" という DXF 名をもつオブジェクトの場合はストラクチャと判断しています。

【AutoCAD Mechanical】部品表にデータを読み込む

部品表にデータを読み込むには

部品表 ダイアログボックス から部品表にデータを読み込ませることができますが、前回の記事と同じように API を使用して部品表にデータを読み込ませることが可能です。

【AutoCAD Mechanical】部品表のデータを書き出す - development of

部品表の読み込みには、AcmBOMManager::importBOM メソッドを使用します。

サンプルコード

#define IMPORT_EXT _T("Microsoft Access(*.mdb)|*.mdb|Microsoft Excel 97(*.xls)|*.xls|Microsoft Excel 95(*.xls)|*.xls|dBase 5(*.dbf)|*.dbf|dBase IV(*.dbf)| *.dbf|dBase III(*.dbf)|*.dbf|タブ区切りのテキスト形式(*.txt)|*.txt|セミコロン区切りのテキスト形式(*.csv)|*.csv|HTML書き出し(*.html)|*.html||")
void ImportBOM()
{
    // ファイルダイアログを表示
    CFileDialog dlg(
        TRUE,
        _T("mdb"),
        NULL, OFN_CREATEPROMPT,
        IMPORT_EXT);
    if (dlg.DoModal() != IDOK) return;

    AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase();
    AcDbObjectId bomId;
    CString bomTableName;

    // 部品表[MAIN]を取得
    acmBomMgr->getBomTable(bomId, bomTableName, pDb->currentSpaceId());

    // 部品表に選択したデータを読み込む
    if (acmBomMgr->importBOM(
        (AcmBOMManager::ImportBOMType)(dlg.m_ofn.nFilterIndex > 6 ? dlg.m_ofn.nFilterIndex + 3 : dlg.m_ofn.nFilterIndex),
        bomId, 
        dlg.GetPathName(), 
        bomTableName, 
        true) == Acad::eOk)
    {
        acutPrintf(_T("\n部品表[%s]の内容を[%s]に基づいて更新しました。"), bomTableName, dlg.GetFileName());
    }
    else
    {
        acutPrintf(_T("\n部品表[%s]の内容のロードに失敗しました。"), bomTableName);
    }
}

【AutoCAD Mechanical】部品表のデータを書き出す

部品表のデータを書き出すには

一般的には部品表 ダイアログ ボックス から部品表の内容を書き出すと思いますが、 f:id:tknmt:20190611173802p:plain API を使用して指定したファイル形式で書き出すことも可能です。

部品表の書き出しには、AcmBOMManager::exportBOM メソッドを使用します。
書き出せるファイル形式は、Microsoft Access (.mdb)Microsoft Excel (.xls)、テキスト形式 (*.txt) などがあります。

サンプルコード

#define EXPORT_EXT _T("Microsoft Access(*.mdb)|*.mdb|Microsoft Access 97(*.mdb)|*.mdb|Microsoft Excel 97(*.xls)|*.xls|Microsoft Excel 95(*.xls)|*.xls|dBase 5(*.dbf)|*.dbf|dBase IV(*.dbf)| *.dbf|dBase III(*.dbf)|*.dbf|タブ区切りのテキスト形式(*.txt)|*.txt|セミコロン区切りのテキスト形式(*.csv)|*.csv|HTML書き出し(*.html)|*.html||")
void ExportBOM()
{
    // ファイルダイアログを表示
    CFileDialog dlg(
        FALSE, 
        _T("mdb"),
        NULL, OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT,
        EXPORT_EXT);
    if (dlg.DoModal() != IDOK) return;
    
    AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase();
    AcDbObjectId bomId;
    CString bomTableName;

    // 部品表[MAIN]を取得
    acmBomMgr->getBomTable(bomId, bomTableName, pDb->currentSpaceId());

    // 部品表を書き出す
    if (acmBomMgr->exportBOM(
        (AcmBOMManager::ExportBOMType)(dlg.m_ofn.nFilterIndex > 7 ? dlg.m_ofn.nFilterIndex + 3 : dlg.m_ofn.nFilterIndex),
        bomId, 
        dlg.GetPathName(),
        bomTableName, 
        true) == Acad::eOk)
    {
        acutPrintf(_T("\n部品表[%s]の内容を[%s]に保存しました。"), bomTableName, dlg.GetFileName());
    }
    else
    {
        acutPrintf(_T("\n部品表[%s]の内容の保存に失敗しました。"), bomTableName);
    }
}

※サンプルコードについての補足

AcmBOMManager::exportBOM メソッドに渡す書き出すファイルの形式として、AcmBOMManager::ExportBOMType 列挙体を使用して指定します。

列挙子 ファイル形式
eEXPORT_MDB 1 Microsoft Access
eEXPORT_MDB97 2 Microsoft Access 97
eEXPORT_EXCEL97 3 Microsoft Excel 97
eEXPORT_EXCEL95 4 Microsoft Excel 95
eEXPORT_DB5 5 dBASE 5
eEXPORT_DBIV 6 dBASE IV
eEXPORT_DBIII 7 dBASE III
eEXPORT_FOXVIS 8 Microsoft Visual Foxpro 3.0
eEXPORT_FOX26 9 Microsoft Foxpro 2.6
eEXPORT_FOX25 10 Microsoft Foxpro 2.5
eEXPORT_TXT 11 タブ区切りのテキスト形式
eEXPORT_CSV 12 セミコロン区切りのテキスト形式
eEXPORT_HTML 13 HTML形式
eEXPORT_ALLFILE 14

サンプルコードでは Foxpro 形式以外の形式で書き出せるようにしています。

【IJCAD】コマンド実行コンテキストについて

コマンド実行コンテキストについて

.NET API でコマンドを定義するときに、IJCAD でも AutoCAD と同じようにコマンド実行コンテキストについて考慮する必要があります。

2つの実行コンテキスト

実行コンテキストには、ドキュメント実行コンテキスト アプリケーション実行コンテキストの2種類があります。

CommandFlags.Modal を指定したコマンドはドキュメント実行コンテキストで、CommandFlags.Session を指定したコマンドはアプリケーション実行コンテキストです。

ドキュメント実行コンテキストのコマンドは、基本的に現在アクティブな図面に対して実行されるコマンドで、コマンドの開始時と終了時は同一の図面がアクティブな状態です。

アプリケーション実行コンテキストのコマンドは、コマンドの処理内でアクティブな図面が変更され、ドキュメントを跨って処理が実行されるコマンドで、コマンドの開始時と終了時でアクティブな図面が異なる場合もあります。

実行コンテキストの使い分け

通常コマンドの実行は、現在アクティブな図面に対して処理を行うために実行されるので、基本的にドキュメント実行コンテキストでコマンドを定義します。

NEW コマンドや OPEN コマンドのように、新しいドキュメントを追加する時や、開いている複数の図面に対して一括に処理を行う時などは、アプリケーション実行コンテキストでコマンドを定義します。

また IJCAD に限った話ですが、Window Forms 内の処理にブレークポイントを追加しても、うまくデバッグできないという問題があります。 f:id:tknmt:20190529105632p:plain この場合にアプリケーション実行コンテキストでコマンドを定義すると、ブレークポイントで処理が止まるようになり、変数の中身などを確認できるようになります。

アプリケーション実行コンテキストの問題点

IJCAD のアプリケーション実行コンテキストにはいくつかの問題点があります。

AutoCAD では、アプリケーション実行コンテキストのコマンドで、図面のデータベースを編集する場合は、明示的にドキュメントをロックしないと例外が発生してしまいますが、IJCAD ではドキュメントをロックしていなくてもデータベースの編集ができてしまいます。

また、ユーザ入力で表示したメッセージなどがコマンド終了時にも表示されたままになったりします。

Window Forms 内のブレークポイントが止まらないというのは、開発を進めるにあたっての障害になりますが、開発が完了しアプリケーションをリリースする際には、可能な範囲でドキュメント実行コンテキストに変更したほうが、品質の高いアプリケーションを世に出せると思います。

【IJCAD】COMの機能をもっと楽に活用する

COM 相互運用機能とは

.NET API で開発を進めるときに、GrxCAD.Interop.dll を参照することで、COM の機能を使用することができるようになります。

COM 相互運用機能を使用することで、過去に VBA を使用して開発を進めていたときのコードを、一部を書き換えるだけで簡単に .NET API へ移植することができます。

COM の機能を活用する

.NET API ではオブジェクト範囲にズームを実装するのが多少面倒なんですが、COM には標準の関数として ZoomExtents メソッドが存在しています。

現在アクティブな空間のオブジェクト範囲を取得して、アクティブなビューをオブジェクト範囲から計算した幅と高さに変更するという処理を実装する位なら、COM の ZoomExtents メソッドを呼び出す方が楽です。

ただ、COM 相互運用機能を使用するために参照する GrxCAD.Interop.dll ですが、このライブラリは 32bit 用と 64bit 用で分けられていて、使用する IJCAD に合わせて参照設定を変更する必要があります。

もっと楽にCOMの機能を使用する

.NET Framework 4.0 から動的言語ランタイムが追加されました。これによって GrxCAD.Interop.dll を参照しなくても、簡単に COM のメソッドを呼び出せるようになりました。

例えば ZoomExtents メソッドを呼び出すには次のようなコードになります。

((dynamic)Application.AcadApplication).ZoomExtents();

Application.AcadApplication プロパティの値を dynamic にキャストして ZoomExtents メソッドを呼び出しています。

【IJCAD】プログレスメーターを表示する

プログレスメーターを表示する

.NET APIプログレスメーターを表示させるには、 ProgressMeter クラスを使用するのですが、IJCAD 2018 までは API に不具合があるのか、プログレスメーターがうまく表示されませんでした。
ただこの問題は IJCAD 2019 では修正されているようです。

サンプルコード

var pm = new ProgressMeter();
pm.Start("プログレスバーサンプル");
pm.SetLimit(100);

for (var i = 0; i <= 100; i++)
{
    System.Threading.Thread.Sleep(10);
    pm.MeterProgress();
}
pm.Stop();

実行結果

IJCAD 2019 では問題なくプログレスメーターを表示できています。 f:id:tknmt:20190527154151g:plain

IJCAD 2018 ではサンプルコードを実行しても何も表示されませんでした。 f:id:tknmt:20190527154246g:plain

【IJCAD】任意の軸のアルゴリズムについて

任意の軸のアルゴリズムとは

任意の軸のアルゴリズムは、特定のオブジェクトで OCS(オブジェクト座標系)を生成するために、CAD の内部で使用されているアルゴリズムです。

例えば、円オブジェクトの OCS が任意の軸のアルゴリズムによって、円の X 軸と Y 軸の方向が決められています。

アルゴリズムの詳細

AutoCAD のヘルプには、任意の軸のアルゴリズムについて次のような感じで説明されています。

  1. オブジェクトの法線ベクトルを N とする。
  2. ベクトル N の X の絶対値と Y の絶対値が、両方とも 1/64 未満の場合は、WCS の Y 軸と法線ベクトル N の外積の値が X 軸になる。 それ以外の場合は WCS の Z 軸と法線ベクトル N の外積の値が X 軸 になる。
  3. 法線ベクトル N と、2 で求めた X 軸のベクトルの外積の値が Y 軸になる。

.NET API だとこんな感じですね。

Vector3d normal = ent.Normal;
Vector3d axisX, axisY;
if (Math.Abs(normal.X) < 1.0 / 64.0 && Math.Abs(normal.Y) < 1.0 / 64.0)
{
    axisX = Vector3d.YAxis.CrossProduct(normal);
}
else
{
    axisX = Vector3d.ZAxis.CrossProduct(normal);
}
axisY = normal.CrossProduct(axisX);

サンプルプロジェクト

任意の軸のアルゴリズムを用いて、円オブジェクト上にカーソルが近づいている時に、円のオブジェクト座標系を表示させすサンプルを作成してみました。

ArbitraryAxisAlgorithmSample.zip - Google ドライブ

実行結果

サンプルを IJCAD 上で動かしてみると次のようになります。


ArbitraryAxisAlgorithmSample