#region Namespaces
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
#endregion
//当ソフトにはEPPlus4.5.3.3を使用しています。これはLGPLライセンスです。著作権はEPPlus Software社です。
namespace CopyAndPasteElement
{
[Transaction(TransactionMode.Manual)]
public class Command : IExternalCommand
{
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements)
{
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);
ElementId elementId = e.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);
//配置方法について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 = KUtil2.GetSpace(doc, uidoc);
if (space == null)
{
TaskDialog.Show("エラー", "スペースが正しく選ばれていません");
return Result.Cancelled;
}
//スペースの原点(左下の最小点)を取得する
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]);
//スペース内に指定台数の器具が納まるか確認する
if(width < equipmentSizeX * wNumber)
{
TaskDialog.Show("エラー", "スペースの横方向に指定台数の器具が納まりません。台数を減らしてください");
return Result.Cancelled;
}
if (depth < equipmentSizeY * dNumber)
{
TaskDialog.Show("エラー", "スペースの縦方向に指定台数の器具が納まりません。台数を減らしてください");
return Result.Cancelled;
}
//横方向の間隔を計算する
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;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CopyAndPasteElement
{
public partial class Form1 : Form
{
int wNumber;//横方向の台数
int dNumber;//縦方向の台数(間口方向)
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
if (!Int32.TryParse(textBox1.Text, out wNumber))
{
this.DialogResult = DialogResult.Cancel;
}
if (!Int32.TryParse(textBox2.Text, out dNumber))
{
this.DialogResult = DialogResult.Cancel;
}
this.DialogResult = DialogResult.OK;
Close();
}
private void button1_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
Close();
}
public int getWNumber()
{
return wNumber;
}
public int getDNumber()
{
return dNumber;
}
private void Form1_Load(object sender, EventArgs e)
{
this.textBox2.Focus();
}
}
}
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CopyAndPasteElement
{
public static class KUtil2
{
//Excelファイルを読むためのダイアログを表示する
public static string OpenExcel()
{
string fileName = string.Empty;
//OpenFileDialog
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Title = "ファイル選択";
openFileDialog.Filter = "ExcelFiles | *.xls;*.xlsx;*.xlsm";
//初期表示フォルダはデスクトップ
openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
//ファイル選択ダイアログを開く
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
fileName = openFileDialog.FileName;
}
}
return fileName;
}
//ミリメーターを内部単位に変換する
public static double CVmmToInt(double x)
{
return UnitUtils.ConvertToInternalUnits(x, UnitTypeId.Millimeters);
}
//内部単位をミリメーターに変換する
public static double CVintTomm(double x)
{
return UnitUtils.ConvertFromInternalUnits(x, UnitTypeId.Millimeters);
}
//目的のレベル(階)を探して返す
public static Level GetLevel(Document doc, string levelName)
{
Level result = null;
//エレメントコレクターの作成
FilteredElementCollector collector = new FilteredElementCollector(doc);
//レベルを全て検出する
ICollection<Element> collection = collector.OfClass(typeof(Level)).ToElements();
//目的のレベルを探す
foreach (Element element in collection)
{
Level level = element as Level;
if (null != level)
{
if (level.Name == levelName)
{
result = level;
}
}
}
return result;
}
//床のためのカーブを作る
public static void CreateGrids(Document doc)
{
double y1 = -100;
double y2 = 30;
double[] x = { 2, 34, 74, 114, 147 };
string[] symbX = { "1", "2", "3", "4", "5" };
double x1 = -30;
double x2 = 180;
double[] y = { -2, -33, -66 };
string[] symbY = { "A", "B", "C" };
for (int i = 0; i < x.Length; i++)
{
XYZ start = new XYZ(x[i], y1, 0);
XYZ end = new XYZ(x[i], y2, 0);
Line line = Line.CreateBound(start, end);
Grid grid = Grid.Create(doc, line);
grid.Name = symbX[i];
}
for (int i = 0; i < y.Length; i++)
{
XYZ start = new XYZ(x1, y[i], 0);
XYZ end = new XYZ(x2, y[i], 0);
Line line = Line.CreateBound(start, end);
Grid grid = Grid.Create(doc, line);
grid.Name = symbY[i];
}
}
//ユーザーがピックアップすることでモデルからスペースを取得する(現在モデルとリンクモデルに対応)
public static Space GetSpaceCurrentAndLink(Document doc, UIDocument uidoc)
{
Reference spaceRef;//スペースのリファレンス
ElementInLinkSelectionFilter<Space> filter = new ElementInLinkSelectionFilter<Space>(doc);
try
{
spaceRef = uidoc.Selection.PickObject(ObjectType.PointOnElement, filter, "配置先のスペースを選んでください(このモデルでもリンクモデルでも)");
}
catch (OperationCanceledException)
{
return null;
}
Space space;
if (filter.LastCheckedWasFromLink)
{
space = filter.LinkedDocument.GetElement(spaceRef.LinkedElementId) as Space;
}
else
{
space = doc.GetElement(spaceRef) as Space;
}
return space;
}
public class ElementInLinkSelectionFilter<T> : ISelectionFilter where T : Element
{
private Document _doc;
public ElementInLinkSelectionFilter(Document doc)
{
_doc = doc;
}
public Document LinkedDocument { get; private set; } = null;
public bool LastCheckedWasFromLink
{
get { return null != LinkedDocument; }
}
public bool AllowElement(Element e)
{
return true;
}
public bool AllowReference(Reference r, XYZ p)
{
LinkedDocument = null;
Element e = _doc.GetElement(r);
if (e is RevitLinkInstance)
{
RevitLinkInstance li = e as RevitLinkInstance;
LinkedDocument = li.GetLinkDocument();
e = LinkedDocument.GetElement(r.LinkedElementId);
}
return e is T;
}
}
//ユーザーがピックアップすることでモデルからスペースを取得する(現在モデルのみ対応)
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;
}
}
//スペースのバウンディングボックスを返す
public static XYZ SpaceMin(Document doc, Space space)
{
Autodesk.Revit.DB.View activeView = doc.ActiveView;
BoundingBoxXYZ box = space.get_BoundingBox(activeView);
XYZ min = box.Min;
return min;
}
public static XYZ SpaceMax(Document doc, Space space)
{
Autodesk.Revit.DB.View activeView = doc.ActiveView;
BoundingBoxXYZ box = space.get_BoundingBox(activeView);
XYZ max = box.Max;
return max;
}
public static string[] SpaceWidthAndDepth(Document doc, Space space)
{
string[] result = new string[2];// width, height
Autodesk.Revit.DB.View activeView = doc.ActiveView;
BoundingBoxXYZ box = space.get_BoundingBox(activeView);
XYZ min = box.Min;
XYZ max = box.Max;
double width = Math.Abs(max.X - min.X);
double depth = Math.Abs(max.Y - min.Y);
result[0] = width.ToString();
result[1] = depth.ToString();
return result;
}
}
}