Revit api 를 활용하여 line 선택시 교차점을 중심으로 line split 한 뒤 Duct 생성하기
[ 방법 ]
1)Line 가져오기
2)교차점을 중심으로 line 자르기
3)Line 잘라졌는지 확인하기 위해 Detail Line 그리기 ( 선택사항 )
4)파이프 생성
ISelectionFilter selectionFilter = new PlanarFacesSelectionFilter(document);
IList<Reference> references = uidoc.Selection.PickObjects(ObjectType.Element, selectionFilter, "Select Multiple planar faces");
List<Element> elementList = new List<Element>();
foreach (Reference reference in references)
{
Element element = uidoc.Document.GetElement(reference);
elementList.Add(element);
}
// 1-3) geometryElements 생성하기
List<Line> lines = new List<Line>();
foreach (Element element in elementList)
{
GeometryElement geometry = element.get_Geometry(new Options());
foreach (GeometryObject geometryObject in geometry)
{
Line line = geometryObject as Line;
lines.Add(line);
}
}
// 교차점이 있는지 확인하기
IntersectionResultArray results = null;
Line line1 = lines[0];
Line line2 = lines[1];
line1.Intersect(line2, out results);
IntersectionResult iResult = results.get_Item(0);
var IntersectPoint = iResult.XYZPoint;
if (IntersectPoint != null)
{
List<Line> splitLines = new List<Line>();
DetailLine detailLine1 = null;
DetailLine detailLine2 = null;
// 교차점 기준으로 라인 자르기
for (int i = 0; i < lines.Count; i++)
{
Curve curve = lines[i];
double paraIntersection = curve.Project(IntersectPoint).Parameter;
double startpam = curve.GetEndParameter(0);
double endpam = curve.GetEndParameter(1);
Curve curve1 = curve.Clone();
Curve curve2 = curve.Clone();
curve1.MakeBound(startpam, paraIntersection);
curve2.MakeBound(paraIntersection, endpam);
splitLines.Add(curve1 as Line);
splitLines.Add(curve2 as Line);
// 평면도에서 교차점 기준으로 잘라낸 선분이 보일 수 있도록 DETAILLINE 생성하기
detailLine1 = document.Create.NewDetailCurve(document.ActiveView, curve1) as DetailLine;
detailLine2 = document.Create.NewDetailCurve(document.ActiveView, curve2) as DetailLine;
}
lines.Clear();
lines = splitLines;
}
[ 주의할점 ]
1.2개의 line 에 교차점이 존재할 경우, 교차점을 기준으로 Curve Class 의 MakeBound를 이용하여 라인을 자른다. 이때, document 상에서는 4등분 된 line 이 보이지 않는다. 만약 document 상에서 split 된 line을 확인하고 싶다면 detailline 혹은 modelline을 생성하여 제대로 잘라졌는지 확인하면된다.
2.Datailline 을 그린 후 selection 할 시 3D 뷰에서는 선택되지 않는다. 따라서, level1과 같은 평면도에서 detail line을 선택해야한다.
//선택한 개체를 필터링할 수 있는 인터페이스 구현(= An interface that provides the ability to filter objects during a selection operation)
public class PlanarFacesSelectionFilter : ISelectionFilter
{
Document doc = null;
public PlanarFacesSelectionFilter(Document document)
{
doc = document;
}
public bool AllowElement(Element elem)
{
return true;
}
public bool AllowReference(Reference reference, XYZ position)
{
if (doc.GetElement(reference).GetGeometryObjectFromReference(reference) is PlanarFace)
{ return true; }
return false;
}
}
public List<Element> GetDuctLine(UIDocument uidoc, Document document)
{
ISelectionFilter selectionFilter = new PlanarFacesSelectionFilter(document);
IList<Reference> references = uidoc.Selection.PickObjects(ObjectType.Element, selectionFilter, "Select Multiple planar faces");
List<Element> elementList = new List<Element>();
foreach (Reference reference in references)
{
Element element = uidoc.Document.GetElement(reference);
elementList.Add(element);
}
return elementList;
}
public void CreateDuct(UIDocument uidoc, Document document, List<Element> elementList)
{
// (1) Duct 생성하기
// 1-1) Duct 생성에 필요한 속성 임의로 가져오기
DuctType ductType = new FilteredElementCollector(document).OfClass(typeof(DuctType)).Cast<DuctType>().Where(a => a.FamilyName.Contains("Oval")).FirstOrDefault();
Level level = new FilteredElementCollector(document).OfClass(typeof(Level)).First() as Level;
List<ElementId> DuctSystemTypelst = new FilteredElementCollector(document).OfCategory(BuiltInCategory.OST_DuctSystem).ToElementIds().ToList();
ElementId systemTypeId = DuctSystemTypelst.FirstOrDefault();
// 1-2) 변수선언
Duct newduct = null;
XYZ start = null;
XYZ end = null;
// 1-3) geometryElements 생성하기
List<GeometryElement> geometryElements = new List<GeometryElement>();
foreach (Element element in elementList)
{
GeometryElement geometry = element.get_Geometry(new Options());
geometryElements.Add(geometry);
}
// 1-4) Transaction 시작
Transaction trans = new Transaction(document);
trans.Start("Create Duct");
// 1-5) Duct 생성 후 리스트 담기
List<Line> lines = new List<Line>();
List<Duct> ducts = new List<Duct>();
List<Element> eleDuct = new List<Element>();
foreach (GeometryElement geometry in geometryElements)
{
foreach (GeometryObject geometryObject in geometry)
{
Line line = geometryObject as Line;
lines.Add(line);
start = line.GetEndPoint(0);
end = line.GetEndPoint(1);
newduct = Duct.Create(document, systemTypeId, ductType.Id, level.Id, start, end);
ducts.Add(newduct);
Element element = document.GetElement(newduct.Id as ElementId);
eleDuct.Add(element);
// 1-6) fitting 할 elbow 굵기에 맞게 duct 굴기 설정하기
Parameter width = element.LookupParameter("Width");
width.Set(10 * 0.09);
Parameter height = element.LookupParameter("Height");
height.Set(10 * 0.09);
}
}
// 1-6) transaction 안에서 duct fitting종류 설정했으므로 중간에 새로고침하여 값 적용되도록 하기
document.Regenerate();
// (2) Duct 연결시키기 ( NewElbowFitting 메소드 사용)
// 2-1) Duct 연결할 elbow Type 지정하기
ElementType elementType = new FilteredElementCollector(document).OfCategory(BuiltInCategory.OST_DuctFitting).OfClass(typeof(ElementType)).Cast<ElementType>().Where(x => x.FamilyName.Equals("M_Oval Elbow - Gored")).FirstOrDefault();
RoutingPreferenceManager rpm = newduct.DuctType.RoutingPreferenceManager;
rpm.AddRule(RoutingPreferenceRuleGroupType.Elbows, new RoutingPreferenceRule(elementType.Id, "Duct Fitting"));
int routingPerenceGroupCnt = rpm.GetNumberOfRules(RoutingPreferenceRuleGroupType.Elbows);
if (routingPerenceGroupCnt > 1)
{
for (int k = 0; k < routingPerenceGroupCnt - 1; k++)
{
rpm.RemoveRule(RoutingPreferenceRuleGroupType.Elbows, 0);
}
}
// 2-2) 연결시킬 덕트 connector 가져오기위해 connectmanager로 connectors 뽑아내기
for (int i = 0; i < ducts.Count() - 1; i++)
{
ConnectorManager duct_connectorManager1 = ducts[i].ConnectorManager;
ConnectorSet duct_connectorSet1 = duct_connectorManager1.Connectors;
ConnectorManager duct_connectorManager2 = ducts[i + 1].ConnectorManager;
ConnectorSet duct_connectorSet2 = duct_connectorManager2.Connectors;
// 2-3) duct 가 가지고 있는 connector 중에서 가장 가까운 connector 뽑아내기
Connector connector1 = null;
Connector connector2 = null;
double minDist = double.MaxValue;
foreach (Connector conn1 in duct_connectorSet1)
{
foreach (Connector conn2 in duct_connectorSet2)
{
double distance = conn1.Origin.DistanceTo(conn2.Origin);
if (distance < minDist)
{
connector1 = conn1;
connector2 = conn2;
minDist = distance;
}
}
}
try
{
document.Create.NewElbowFitting(connector1, connector2);
}
catch (Exception ex)
{
TaskDialog.Show("오류창", "Duct fitting 에 실패했습니다.\n파이프간의 각도가 너무 크거나 작지 않은지 확인해 주세요\n\n [ 자세한 오류 메세지 ] \n " + ex.Message);
trans.RollBack();
}
}
// (3) duct 와 겹치는 line 삭제하기
foreach (Element element in elementList)
{
ElementId elementId = element.Id;
document.Delete(elementId);
}
trans.Commit();
}
[ 주의할점 ]
Revit api 를 활용하여 Duct Fitting을 할 경우 Duct 끼리 이어질 Elbow 의 기본값이 None 으로 지정된다.
따라서, Duct 생성후 Routing Preferences 에서 어떤 Elbow와 Fitting시킬 것인지 RoutingPreferenceManager api 를 이용하여 elbowType 을 지정해준다.
Revit api 를 활요하여 Pipe를 생성하고 Elbow를 이용하여 Pipe Fitting 처리한다.
[ 방법 ]
1) UIdocument 에서 선택한 modelLine을 가져온다. 2) line에 맞춰 Pipe 생성한다. 3) 생성된 Pipe 끼리 elbow로 Fitting 시킨다. 4) line 삭제한다.
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
class CreatePipeButton : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
App.m_App = commandData.Application;
UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
Document document = uidoc.Document;
// 선택한 Line 가져오기
List<Element> elementlist = GetPipeLine(uidoc, document);
// Pipe 생성 및 Pipe Fitting
CreatePipe(uidoc, document, elementlist);
return Result.Succeeded;
}
//선택한 개체를 필터링할 수 있는 인터페이스 구현(= An interface that provides the ability to filter objects during a selection operation)
public class PlanarFacesSelectionFilter : ISelectionFilter
{
Document doc = null;
public PlanarFacesSelectionFilter(Document document)
{
doc = document;
}
public bool AllowElement(Element element)
{
return true;
}
public bool AllowReference(Reference refer, XYZ point)
{
if (doc.GetElement(refer).GetGeometryObjectFromReference(refer) is PlanarFace)
{
return true;
}
return false;
}
}
public List<Element> GetPipeLine(UIDocument uidoc, Document document)
{
ISelectionFilter selFilter = new PlanarFacesSelectionFilter(document);
IList<Reference> references = uidoc.Selection.PickObjects(ObjectType.Element, selFilter, "Select multiple planar faces");
List<Element> elementlist = new List<Element>();
foreach (Reference referen in references)
{
Element element = uidoc.Document.GetElement(referen);
elementlist.Add(element);
}
return elementlist;
}
public void CreatePipe(UIDocument uidoc, Document document, List<Element> elementlist)
{
// (1) pipe 생성하기
// 1-1) pipe 생성에 필요한 속성 임의로 가져오기
PipeType pipeType = new FilteredElementCollector(document).OfClass(typeof(PipeType)).FirstElement() as PipeType;
Level level = new FilteredElementCollector(document).OfClass(typeof(Level)).First() as Level;
FilteredElementCollector sysCollector = new FilteredElementCollector(document);
sysCollector.OfClass(typeof(PipingSystemType));
ElementId pipeSysTypeId = sysCollector.FirstElementId();
bool flag = true;
// 1-2) 변수선언
Pipe newpipe = null;
XYZ start = null;
XYZ end = null;
// 1-3) geometryElements 생성하기
List<GeometryElement> geometryElements = new List<GeometryElement>();
foreach (Element element in elementlist)
{
GeometryElement geometry = element.get_Geometry(new Options());
geometryElements.Add(geometry);
}
// 1-4) 트랜젝션 실행
using (Transaction trans = new Transaction(document))
{
try
{
trans.Start("Create pipe");
// 1-5) 파이프 생성 후 리스트에 담기
List<Line> lines = new List<Line>();
List<Pipe> pipes = new List<Pipe>();
List<Element> elePipe = new List<Element>();
foreach (GeometryElement geometryele in geometryElements)
{
foreach (GeometryObject obj in geometryele)
{
Line line = obj as Line;
lines.Add(line);
start = line.GetEndPoint(0);
end = line.GetEndPoint(1);
if (pipeType != null)
{
newpipe = Pipe.Create(document, pipeSysTypeId, pipeType.Id, level.Id, start, end);
pipes.Add(newpipe);
Element element = document.GetElement(newpipe.Id as ElementId);
elePipe.Add(element);
// 1-6) fitting 할 elbow 굵기에 맞게 pipe 굵기 설정하기
ElementId elementId = newpipe.Id as ElementId;
Parameter parameter = element.LookupParameter("Diameter");
parameter.Set(10 * 0.007333);
// 1-7) Pipe 의 연결할 elbow Type 지정하기
// Revit api 로 pipe fitting 할 경우 연결할 elbowType의 기본값이 none이기 때문에 꼭! Routing Preferences에서 설정해주어야한다.
ElementType elbowType = new FilteredElementCollector(document).OfCategory(BuiltInCategory.OST_PipeFitting).OfClass(typeof(ElementType)).Cast<ElementType>().Where(x => x.FamilyName.Contains("M_Elbow")).FirstOrDefault();
RoutingPreferenceManager rpm = newpipe.PipeType.RoutingPreferenceManager;
rpm.AddRule(RoutingPreferenceRuleGroupType.Elbows, new RoutingPreferenceRule(elbowType.Id, "Set Elbow fitting Type"));
int routingPerenceGroupCnt = rpm.GetNumberOfRules(RoutingPreferenceRuleGroupType.Elbows);
if (routingPerenceGroupCnt > 1)
{
for (int k = 0; k < routingPerenceGroupCnt - 1; k++)
{
rpm.RemoveRule(RoutingPreferenceRuleGroupType.Elbows, 0);
}
}
}
}
}
// (2) Pipe 연결시키기
// 2-1) transaction 안에서 종류 설정했으므로 중간에 새로고침하여 elbow 종류 설정한 값 적용되도록 하기
document.Regenerate();
// 2-2) 연결시킬 2개의 파이프 connector 가져오기위해, connectormanager로 connectors 뽑아내기
for (int i = 0; i < pipes.Count() - 1; i++)
{
ConnectorManager pipe_connectorManager1 = pipes[i].ConnectorManager;
ConnectorSet pipe_connectorSet1 = pipe_connectorManager1.Connectors;
ConnectorManager pipe_connectorManager2 = pipes[i + 1].ConnectorManager;
ConnectorSet pipe_connectorSet2 = pipe_connectorManager2.Connectors;
// 2-3) 파이프가 가지고 있는 connectors 에서 연결시킬 connector 뽑아내기
Connector pipe_connector1 = null;
Connector pipe_connector2 = null;
double minDist = double.MaxValue;
foreach (Connector connector1 in pipe_connectorSet1)
{
foreach (Connector connector2 in pipe_connectorSet2)
{
double d = connector1.Origin.DistanceTo(connector2.Origin);
if (d < minDist)
{
pipe_connector1 = connector1;
pipe_connector2 = connector2;
minDist = d;
}
}
}
// 2-4) 2개의 파이프 연결시키기
FamilyInstance fitting = document.Create.NewElbowFitting(pipe_connector1, pipe_connector2);
}
// (3) pipe와 겹치는 line 삭제하기
foreach (Element element in elementlist)
{
ElementId elementId = element.Id;
document.Delete(elementId);
}
trans.Commit();
}
catch (Exception ex)
{
MessageBox.Show("Pipe 간의 각도가 너무 크거나 작습니다. \n조정 후 다시 시도해주세요");
trans.RollBack();
}
}
}
[ 주의할점 ]
Revit api 를 활용하여 Pipe Fitting을 할 경우 Pipe 끼리 이어질 Elbow 의 기본값이 None 으로 지정된다.
따라서, Pipe 생성후 Routing Preferences 에서 어떤 Elbow와 Fitting시킬 것인지 RoutingPreferenceManager api 를 이용하여 elbowType 을 지정해준다.