Revitアドインで設備機器を部屋に自動配置する #002

照明、空調機、制気口などは部屋の中にたくさん配置することが多いですね。台数が多いので位置揃え作業も時間がかかります。そこでその単純作業をRevitアドインで自動化してみたいと思います。

手順

  • 部屋にはRevitのスペースを配置しておく
  • 設備機器を当該スペースに1台、手動で配置
  • アドインを実行
  • 上記の1台の機器を選択
  • 縦横の台数を指定
  • スペースを選択
  • その結果、スペース内に機器が自動配置される

アドインを実行し機器を選ぶ

照明器具1台を選択します

 

台数を指定

この例では縦横の台数を人間が指定しています。別の案としては次のようなものも考えられます

  • 総台数だけを指定しプログラムで縦横を最適に決定する
  • 照度計算書がExcelであればそれを読み込み配置決定する
  • 台数はここでは決定せず配置するスペースを選択したときに平均照度法で台数決める

 

配置するスペースを選択

 

自動配置される

器具の水平配置は通常われわれが行っているように均等に配置されるように計算しています。器具の高さは最初の1台の高さを継承しています

 

アドインプログラムの解説

最初の1台の選択

器具の選択はPickObjectメソッドでできます。エレメントIDはRevitのすべてのオブジェクトに付けられる識別子です。これは要素をコピーするときに使います

UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
Document doc = uidoc.Document;

//コピーする器具を選ぶ--------------------------------------
Reference sel = uidoc.Selection.PickObject(ObjectType.Element,"コピーする器具を選んでください");
if (sel == null)
{
    TaskDialog.Show("エラー", "コピーする要素が正しく選ばれていません");
    return Result.Cancelled;
}
Element e = doc.GetElement(sel);//Referenceから要素を取得
ElementId elementId = e.Id;//エレメントIDを取得

器具の位置、サイズを取得

器具を移動するときに現在位置が必要です。器具を縦横に並べるときに、場合によって器具がおさまらないこともあるので器具サイズを取得して後で空間をチェックします。

//現在のエレメントの位置
LocationPoint Lp = e.Location as LocationPoint;
XYZ ElementPoint = Lp.Point as XYZ;

//器具サイズ
double equipmentSizeX = Math.Abs(e.get_BoundingBox(doc.ActiveView).Max.X -
    e.get_BoundingBox(doc.ActiveView).Min.X);
double equipmentSizeY = Math.Abs(e.get_BoundingBox(doc.ActiveView).Max.Y -
    e.get_BoundingBox(doc.ActiveView).Min.Y);

ユーザーから配置台数をきく

Form1はWindows Formで作ります。

このフォームを使って台数を取得します。横方向の台数wNumber、縦方向の台数dNumberに結果が入ります

//配置方法についてFormでユーザー入力---縦と横の台数を聞く------------------
//横方向の台数、初期化
int wNumber = 0;
//縦方向の台数、初期化
int dNumber = 0;

using (Form1 thisForm = new Form1())
{
    if (thisForm.ShowDialog() == DialogResult.OK)
    {
        //OKボタンが押されたら入力内容を取り込む
        //横方向(幅方向)の台数
        wNumber = thisForm.getWNumber();
        //縦方向(高さ方向)の台数
        dNumber = thisForm.getDNumber();
        //数字の適正チェック
        if(wNumber < 1)
        {
            TaskDialog.Show("エラー", "横方向の台数は1以上でなければなりません");
            return Result.Cancelled;
        }
        if (dNumber < 1)
        {
            TaskDialog.Show("エラー", "縦方向の台数は1以上でなければなりません");
            return Result.Cancelled;
        }
    }
    else
    {
        return Result.Cancelled;
    }
}

スペースを選んでもらう

取得したスペースは変数spaceに入ります

//配置するスペースを選ぶ
Space space = KUtil2.GetSpace(doc, uidoc);
if (space == null)
{
    TaskDialog.Show("エラー", "スペースが正しく選ばれていません");
    return Result.Cancelled;
}
        //ユーザーがピックアップすることでモデルからスペースを取得する(現在モデルのみ対応)
        public static Space GetSpace(Document doc, UIDocument uidoc)
        {
            try
            {
                Reference spaceRef = uidoc.Selection.PickObject(
                    ObjectType.Element,
                    new SpaceSelectionFilter(),
                    "配置先のスペースを選んでください");

                return doc.GetElement(spaceRef) as Space;
            }
            catch (OperationCanceledException)
            {
                //ユーザーがキャンセルした場合
                return null;
            }
        }

        public class SpaceSelectionFilter : ISelectionFilter
        {
            public bool AllowElement(Element elem)
            {
                return elem is Space;
            }

            public bool AllowReference(Reference reference, XYZ position)
            {
                return false;
            }
        }

スペースの座標とサイズを計算

Revitの座標は平面をみたときに左から右にX座標軸、下から上にY座標軸があります。スペースには最小座標の点と最高座標の点があり、スペースはその間に存在します。左下が最小点です。このアドインで欲しい数値はスペースの幅と奥行きです。次のように取得します

//スペースの原点(左下の最小点)を取得する
XYZ origin = KUtil2.SpaceMin(doc, space);

//スペースの幅widthと奥行depth
string[] wd = KUtil2.SpaceWidthAndDepth(doc, space);
double width = Convert.ToDouble(wd[0]);
double depth = Convert.ToDouble(wd[1]);

 

器具をコピーして配置

コピーの手順は次のとおり、最初の1台を計算した左下の点に移動する、それを横一列分だけコピーする、横一列を縦方向にコピーする。間隔は全てスペースから割り出した適正な位置

//横方向の間隔を計算する
double wDistance = width / wNumber;
//縦方向の間隔を計算する
double dDistance = depth / dNumber;

//左下の最初要素の位置を決定する。
double lowerLevelOfSpace = space.Level.Elevation;//面付ではない器具を扱うとき、器具の高さ情報はGL面(基準面)からの高さになるのでスペースの下限レベルを引く
XYZ newPosition = origin.Add(new XYZ(wDistance/2, dDistance/2, ElementPoint.Z - lowerLevelOfSpace));//高さ方向は現在の器具高さ採用

//最初の器具を移動させるための移動ベクトルを計算
XYZ moveVector = newPosition - ElementPoint;

//コピーして配置する
using (Transaction tx = new Transaction(doc))
{
    tx.Start("Copy Elements in Space");

    //コピー元器具1個を移動ベクトル分だけ移動
    ElementTransformUtils.MoveElement(doc, elementId, moveVector);

    //まず横方向のコピー。あとでこの1列を縦方向にコピーする
    //横の台数のほうが多いケースがおおいのではないか。部屋が横に長い(推測)
    IList<ElementId> elementsToCopy = new List<ElementId>();//横一列の器具のIDたち
    elementsToCopy.Add(elementId);  // 最初の器具を追加

    //横方向のコピー
    for (int i = 1; i < wNumber; i++)  // i=1から開始し、wNumber未満まで
    {
        XYZ copyVector = new XYZ(i * wDistance, 0, 0);
        ICollection<ElementId> copiedElementIds = ElementTransformUtils.CopyElement(doc, elementId, copyVector);
        // コピーされた器具のElementIdをリストに追加
        foreach (ElementId copiedElementId in copiedElementIds)
        {
            elementsToCopy.Add(copiedElementId);
        }
    }
    //縦方向のコピー
    for (int j = 1; j < dNumber; j++)
    {
        //コピー元からコピー先までの移動距離ベクトル。ここではY方向のみ
        XYZ copyVector = new XYZ(0, j * dDistance, 0);
        
        ElementTransformUtils.CopyElements(doc, elementsToCopy, copyVector);
    }

    tx.Commit();
    
}
return Result.Succeeded;

まとめ

同じ器具を多数並べる、位置揃えをするといった単純作業はアドインを使うのが効率的です。ポイントになるのが総台数をどのように決定するかです。手動か、自動化か?

全体のコードに興味がある方はこちらに掲載しています

githubはこちら。https://github.com/katsumikawasaki/CopyAndPasteElement.git

(ご注意)ここに掲載したコードはひとつの技術的な解法を示すサンプルです。実践的な用途に耐えうる堅牢性や品質等は備えていません。コードの実行結果について当組織は一切責任を負いませんので、参照、利用はご自身の責任でお願いします。

書籍もぜひご覧ください


コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です