[ 목표 ]

Revit api 를 활용하여 line 선택시 자동으로 Duct Cross Fitting 하기

 

[ 방법 ]

1) UIdocument 에서 선택한 modelLine을 가져온다.

2) line에 맞춰 Duct 생성한다.

3) Duct 끼리 Intersect 되는 부분이 connector와 일치할 경우

- 원하는 elbowfitting시킨다.

- Newelbowfitting 메소드 사용

3) Duct 끼리 Intersect 되는 부분이 connector와 일치하지 않을 경우

교차점을 기준으로 파이프 자른다.

- intersecNewCrossFitting 메소드 이용한다.

원하는 crosses Fitting 시킨다.

4) line 삭제한다.

 

        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 = GetDuctLine(uidoc, document);
            CreateDuct(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 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);
            List<Element> elementList = new List<Element>();
            if (MessageBox.Show("Duct 생성하시려면 line 을 선택 후 \n좌측 하단의 Finsh 버튼을 클릭해주세요", "Duct 생성확인", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
            {
                IList<Reference> references = uidoc.Selection.PickObjects(ObjectType.Element, selectionFilter, "Select Multiple planar faces");
                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;
            XYZ splitpoint = null;
            // 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);
                }

            }

            // 1-4) Transaction 시작

            Transaction trans = new Transaction(document);
            trans.Start("Create Duct");

            // 1-5) Duct 생성 후 리스트 담기

            List<Duct> ducts = new List<Duct>();
            List<Element> eleDuct = new List<Element>();

            foreach (Line line in lines)
            {
                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.2);
                Parameter height = element.LookupParameter("Height");
                height.Set(10 * 0.2);
            }

            // 1-6) transaction 안에서 duct fitting종류 설정했으므로 중간에 새로고침하여 값 적용되도록 하기
            document.Regenerate();

            // (2) Duct 연결시키기 ( NewElbowFitting 메소드 사용)
            for (int i = 0; i < ducts.Count(); i++)
            {

                // 교차점이 있는지 확인하기
                int num = 0;
                List<int> numlist = new List<int>();
                IntersectionResultArray result = null;
                List<IntersectionResultArray> results = new List<IntersectionResultArray>();

                for (int j = i + 1; j < lines.Count; j++)
                {
                    Line line1 = lines[i];
                    Line line2 = lines[j];

                    line1.Intersect(line2, out result);
                    if (result != null)
                    {
                        results.Add(result);
                        num = j;
                        numlist.Add(num);
                        result = null;
                    }
                }

                if (results != null)
                {
                    for (int k = 0; k < results.Count; k++)
                    {
                        IntersectionResult iResult = results[k].get_Item(0);
                        XYZ IntersectPoint = iResult.XYZPoint;

                        // 2-2) 연결시킬 덕트 connector 가져오기위해 connectmanager로 connectors 뽑아내기
                        ConnectorManager duct_connectorManager1 = ducts[i].ConnectorManager;
                        ConnectorSet duct_connectorSet1 = duct_connectorManager1.Connectors;
                        //ConnectorManager duct_connectorManager2 = ducts[i + 1].ConnectorManager;
                        ConnectorManager duct_connectorManager2 = ducts[numlist[k]].ConnectorManager;
                        ConnectorSet duct_connectorSet2 = duct_connectorManager2.Connectors;

                        // 2-3) duct 가 가지고 있는 connector 중에서 가장 가까운 connector 뽑아내기
                        Connector connector1 = null;
                        Connector connector2 = null;
                        double minDist = double.MaxValue;
                        bool iscross = false;

                        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;
                                }
                            }
                        }

                        // 2-4) crossfitting 여부확인을 위해, 교차점이 있는지 & 교차점이 connector 와 일치하는지 확인
                        if (connector1.Origin.DistanceTo(IntersectPoint) > 0.001)
                        {
                            iscross = true;
                        }

                        // (3) 파이프끼리 크로스되지 않고 연결되있을때
                        if (iscross == false)
                        {
                            try
                            {
                                // 3-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 t = 0; t < routingPerenceGroupCnt - 1; t++)
                                    {
                                        rpm.RemoveRule(RoutingPreferenceRuleGroupType.Elbows, 0);
                                    }
                                }

                                document.Create.NewElbowFitting(connector1, connector2);
                            }
                            catch (Exception ex)
                            {
                                if (ex.Message.Contains("them is too small or too large"))
                                {
                                    TaskDialog.Show("오류창", "Duct 간의 각도가 너무 크거나 작습니다.\n조정후다시 시도해주세요");
                                }
                                else
                                {
                                    TaskDialog.Show("오류창", "Duct fitting 에 실패했습니다.\n\n [ 자세한 오류 메세지 ] \n " + ex.Message);
                                }
                               
                            }
                        }

                        // (3)파이프끼리 크로스되어 있을때
                        else
                        {
                            // 3-1) 파이프 자르기
                            ElementId newDuctId1 = Autodesk.Revit.DB.Mechanical.MechanicalUtils.BreakCurve(document, ducts[i].Id, IntersectPoint);
                            ElementId newDuctId2 = Autodesk.Revit.DB.Mechanical.MechanicalUtils.BreakCurve(document, ducts[numlist[k]].Id, IntersectPoint);
                            Duct splitduct1 = document.GetElement(newDuctId1) as Duct;
                            Duct splitduct2 = document.GetElement(newDuctId2) as Duct;

                            document.Regenerate();
                            // 3-2) 연결시킬 덕트 connector 가져오기위해 connectmanager로 connectors 뽑아내기

                            ConnectorManager duct_connectorManager3 = splitduct1.ConnectorManager;
                            ConnectorSet duct_connectorSet3 = duct_connectorManager3.Connectors;
                            ConnectorManager duct_connectorManager4 = splitduct2.ConnectorManager;
                            ConnectorSet duct_connectorSet4 = duct_connectorManager4.Connectors;

                            //// 3-3) duct 가 가지고 있는 connector 중에서 가장 가까운 connector 뽑아내기
                            Connector connector3 = null;
                            Connector connector4 = null;

                            double minDist2 = double.MaxValue;
                            foreach (Connector conn3 in duct_connectorSet3)
                            {
                                foreach (Connector conn4 in duct_connectorSet4)
                                {
                                    double distance2 = conn3.Origin.DistanceTo(conn4.Origin);
                                    if (distance2 < minDist2)
                                    {
                                        connector3 = conn3;
                                        connector4 = conn4;
                                        minDist2 = distance2;
                                    }
                                }
                            }
                            // 3-4) duct 연결시키기
                            try
                            {
                                // 3-5) Duct 연결할 elbow Type 지정하기
                                ElementType elementType = new FilteredElementCollector(document).OfCategory(BuiltInCategory.OST_DuctFitting).OfClass(typeof(ElementType)).Cast<ElementType>().Where(x => x.FamilyName.Equals("M_Oval Cross - Straight")).FirstOrDefault();
                                RoutingPreferenceManager rpm = newduct.DuctType.RoutingPreferenceManager;
                                rpm.AddRule(RoutingPreferenceRuleGroupType.Crosses, new RoutingPreferenceRule(elementType.Id, "Duct Fitting"));
                                int routingPerenceGroupCnt = rpm.GetNumberOfRules(RoutingPreferenceRuleGroupType.Crosses);
                                if (routingPerenceGroupCnt > 1)
                                {
                                    for (int t = 0; t < routingPerenceGroupCnt - 1; t++)
                                    {
                                        rpm.RemoveRule(RoutingPreferenceRuleGroupType.Crosses, 0);
                                    }
                                }
                                
                                document.Create.NewCrossFitting(connector1, connector3, connector2, connector4);                               
                            }
                            catch (Exception ex)
                            {
                                if (ex.Message.Contains("them is too small or too large"))
                                {
                                    TaskDialog.Show("오류창", "Duct 간의 각도가 너무 크거나 작습니다.\n조정후다시 시도해주세요");
                                }
                                else
                                {
                                    TaskDialog.Show("오류창", "Duct fitting 에 실패했습니다.\n\n [ 자세한 오류 메세지 ] \n " + ex.Message);
                                }
                            }
                        }
                    }
                }
            }
            //// (4) duct 와 겹치는 line 삭제하기
            foreach (Element element in elementList)
            {
                ElementId elementId = element.Id;
                document.Delete(elementId);
            }
            trans.Commit();
        }

 

 

[ 주의할점 ]

1. Line 그리는 순서에 상관없이 fitting이 될 수 있도록 logic 구성해야한다.

2. Elbow fitting을 할지 creoss fitting을 할지는 connector와 일치하는지 방법 등등으로 확인하여 진행한다.

반응형

오류원인: 

 

ElbowFitting 할때 , Routing Preference의 Elbow 종류가 설정되어 있지 않다.

 

해결방법 : 

 

Element 의 Type Edit > Routing Preferences Edit 클릭 >  Elbow Group에서 원하는 Fitting 종류를 지정한다.

 

반응형

[ 목표 ]

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 을 지정해준다.

반응형

+ Recent posts