using System; using System.Collections.Generic; using System.Linq; using Autodesk.Revit.UI; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Plumbing; using System.Windows.Forms; using Autodesk.Revit.DB.Mechanical; using KDCS.Utils; using KMBIM.Revit.Tools; namespace KMBIM { [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] class PartSpacing : IExternalCommand { UIApplication uiapp; UIDocument uidoc; Autodesk.Revit.DB.Document doc; Autodesk.Revit.Creation.Application creApp; Autodesk.Revit.Creation.Document creDoc; Autodesk.Revit.DB.View view; ExternalCommandData m_commandData; ElementId PipeTypeId = null; ElementId PipeSystemTypeId = null; ElementId PipeLevelId = null; public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { if (!WorkMain.GetInstance().IsValid) return Autodesk.Revit.UI.Result.Succeeded; uiapp = commandData.Application; uidoc = uiapp.ActiveUIDocument; doc = uidoc.Document; m_commandData = commandData; creApp = uiapp.Application.Create; creDoc = doc.Create; view = doc.ActiveView; bool b_Cap = false, b_Reducer = false; double m_Dist = 0; List m_SelElemLst = new List(); List m_SelTeeLst = new List(); List m_SelRDCLst = new List(); List m_SelCapLst = new List(); try { MainForm dlg = new MainForm(); dlg.Cap = true; dlg.Reducer = true; dlg.ShowDialog(); if (dlg.DialogResult == DialogResult.Cancel) return Result.Succeeded; b_Cap = dlg.Cap; b_Reducer = dlg.Reducer; m_Dist = dlg.Dist; IList pickrefs = commandData.Application.ActiveUIDocument.Selection.PickObjects(Autodesk.Revit.UI.Selection.ObjectType.Element); //선택한 객체들 Element로 리스트에 저장 if (pickrefs != null) { foreach (Reference refer in pickrefs) { ElementId elemID = refer.ElementId; Element selElem = doc.GetElement(elemID); m_SelElemLst.Add(selElem); } } else return Result.Succeeded; //선택된 객체중 패밀리 레듀셔,캡을 리스트에 모음. foreach (Element e in m_SelElemLst) { if (e is FamilyInstance) { FamilyInstance family = e as FamilyInstance; var param = family.Symbol.Family.get_Parameter(BuiltInParameter.FAMILY_CONTENT_PART_TYPE); if ((PartType)param.AsInteger() == PartType.Transition) m_SelRDCLst.Add(family); else if ((PartType)param.AsInteger() == PartType.Cap) m_SelCapLst.Add(family); } } if (b_Reducer) { foreach (Element Reducer in m_SelRDCLst) { Set_Reducer_Distance(Reducer, m_Dist); } } if (b_Cap) { foreach (Element Cap in m_SelCapLst) { Set_Cap_Distance(Cap, m_Dist); } } } catch (Exception e) { //MessageBox.Show("" + e); } return Result.Succeeded; } public void Set_Cap_Distance(Element Elem, double Dist) { if (Elem is FamilyInstance) { ConnectorSet FamilyConSet = (Elem as FamilyInstance).MEPModel.ConnectorManager.Connectors; Connector BaseCon = null; //캡은 커넥터가 하나이기 때문에 바로 BaseCon으로 지정. foreach (Connector con in FamilyConSet) { BaseCon = con; } //캡 커넥터에 붙은 객체 커넥터 Connector NextCon = GetNextElementConnector(BaseCon); if (NextCon.Owner is FamilyInstance) { } else if (NextCon.Owner is Pipe) { //파이프 반대쪽 커넥터 Connector NextOtherCon = GetOtherConnector(NextCon); //캡에 붙은 파이프 커넥터 ->파이프 반대 커넥터 방향 XYZ DirecPt = (NextOtherCon.Origin - NextCon.Origin).Normalize(); XYZ sp_pipe = null, ep_pipe = null; Util.GetStartEndPoint(NextCon.Owner as Pipe, ref sp_pipe, ref ep_pipe); double PipeDist = sp_pipe.DistanceTo(ep_pipe); using(Transaction trans = new Transaction(doc)) { trans.Start("Cap"); MoveElement(doc, BaseCon.Owner, NextCon.Origin, DirecPt, Unit.FeetToMM(PipeDist) - Dist); trans.Commit(); } } } } // Reducer 간격 맞추기. public void Set_Reducer_Distance(Element Elem, double Dist) { try { if (Elem is FamilyInstance) { List m_ElemConLst = Util.GetElementConnectors(Elem); //파이프 타입 찾기 위해 근처 파이프 찾기 Connector cntr = null; m_ElemConLst.First(); foreach (Connector con in m_ElemConLst) { cntr = con; while (true) { Connector Nextcntr = Util.GetNextElementConnector(cntr); if (Nextcntr == null) break; if (Nextcntr.Owner is Pipe) { Pipe pp = Nextcntr.Owner as Pipe; PipeSystemTypeId = pp.MEPSystem.GetTypeId(); PipeTypeId = pp.PipeType.Id; PipeLevelId = pp.ReferenceLevel.Id; break; } Connector NextOtherCntr = Util.GetOtherConnector(Nextcntr); if (NextOtherCntr == null) break; cntr = NextOtherCntr; }//while end } //레듀셔의 큰쪽을 Base로 작업 //레듀셔 큰->작은 방향 Connector WideCon = null; Connector narrowCon = null; if (m_ElemConLst.Count == 2) { if (m_ElemConLst[0].Radius > m_ElemConLst[1].Radius) { WideCon = m_ElemConLst[0]; narrowCon = m_ElemConLst[1]; } else { WideCon = m_ElemConLst[1]; narrowCon = m_ElemConLst[0]; } } double Wide2FamDist = 0; double Narrow2FamDist = 0; //레듀셔 관경 큰쪽에 붙은 객체 Connector WideNextCon = Util.GetNextElementConnector(WideCon); if (WideNextCon.Owner is FamilyInstance) { Wide2FamDist = WideCon.Origin.DistanceTo(WideNextCon.Origin); } else if (WideNextCon.Owner is Pipe) { Connector PipeOtherCon = Util.GetOtherConnector(WideNextCon); Connector NextFamilyCon = Util.GetNextElementConnector(PipeOtherCon); Wide2FamDist = WideCon.Origin.DistanceTo(NextFamilyCon.Origin); } //레듀셔 관경 작은쪽에 붙은 객체 Connector NarrowNextCon = Util.GetNextElementConnector(narrowCon); if (NarrowNextCon.Owner is FamilyInstance) { Narrow2FamDist = narrowCon.Origin.DistanceTo(NarrowNextCon.Origin); } else if (NarrowNextCon.Owner is Pipe) { Connector PipeOtherCon = Util.GetOtherConnector(NarrowNextCon); Connector NextFamilyCon = Util.GetNextElementConnector(PipeOtherCon); if (NextFamilyCon != null) Narrow2FamDist = narrowCon.Origin.DistanceTo(NextFamilyCon.Origin); else Narrow2FamDist = narrowCon.Origin.DistanceTo(PipeOtherCon.Origin); } //큰 관경부터 패밀리까지 거리와 작은 관경부터 패밀리까지 거리의 합보다 //간격 조정 거리 X 2가 더크면 작도 안함. if (Wide2FamDist + Narrow2FamDist < Unit.MMToFeet(Dist*2)) return; //거리가 좁은쪽으로 작업 if (Wide2FamDist > Narrow2FamDist)//관경 좁은쪽 작업 { if (NarrowNextCon.Owner is FamilyInstance) { using (Transaction trans = new Transaction(doc)) { trans.Start("MoveEndCreate"); //레듀셔와 패밀리 커넥터 끊기 narrowCon.DisconnectFrom(NarrowNextCon); XYZ Vec = (WideCon.Origin - narrowCon.Origin).Normalize(); MoveElement(doc, narrowCon.Owner, narrowCon.Origin, Vec, Dist); // trans.Commit(); //} // //using (Transaction trans = new Transaction(doc)) //{ // trans.Start("CreatePipe"); Pipe newPipe = Pipe.Create(doc, PipeSystemTypeId, PipeTypeId, PipeLevelId, narrowCon.Origin, NarrowNextCon.Origin); List NewPipeConLst = Util.GetElementConnectors(newPipe); NewPipeConLst.First().Radius = narrowCon.Radius; doc.Regenerate(); //생성된 파이프의 커넥터들 연결 foreach (Connector con in NewPipeConLst) { if (con.Origin.IsAlmostEqualTo(narrowCon.Origin, Unit.MMToFeet(0.1))) con.ConnectTo(narrowCon); else if (con.Origin.IsAlmostEqualTo(NarrowNextCon.Origin, Unit.MMToFeet(0.1))) con.ConnectTo(NarrowNextCon); } trans.Commit(); } } else if (NarrowNextCon.Owner is Pipe) { Connector PipeOtherCon = Util.GetOtherConnector(NarrowNextCon); Connector NextFamilyCon = Util.GetNextElementConnector(PipeOtherCon); if (NextFamilyCon.Owner is FamilyInstance && NextFamilyCon != null) { Line pipeline = (PipeOtherCon.Owner.Location as LocationCurve).Curve as Line; double pipeDist = pipeline.GetEndPoint(0).DistanceTo(pipeline.GetEndPoint(1)); using (Transaction trans = new Transaction(doc)) { trans.Start("move"); narrowCon.DisconnectFrom(NarrowNextCon); //간격 조정 거리보다 레듀셔에 붙은 파이프길이가 클 경우 if (pipeDist > Unit.MMToFeet(Dist)) { XYZ Vec = (NextFamilyCon.Origin - narrowCon.Origin).Normalize(); //레듀셔 위치조정 MoveElement(doc, narrowCon.Owner, narrowCon.Origin, Vec, Math.Abs(Unit.FeetToMM(pipeDist) - Dist)); //파이프 위치조정 MoveElement(doc, NarrowNextCon.Owner, NarrowNextCon.Origin, Vec, Math.Abs(Unit.FeetToMM(pipeDist) - Dist)); } else { XYZ Vec = (narrowCon.Origin - NextFamilyCon.Origin).Normalize(); //레듀셔 위치조정 MoveElement(doc, narrowCon.Owner, narrowCon.Origin, Vec, Math.Abs(Unit.FeetToMM(pipeDist) - Dist)); //파이프 위치조정 MoveElement(doc, NarrowNextCon.Owner, NarrowNextCon.Origin, Vec, Math.Abs(Unit.FeetToMM(pipeDist) - Dist)); } //커넥터 다시 잇기 narrowCon.ConnectTo(NarrowNextCon); trans.Commit(); } } } } else//관경 큰쪽 작업 { if (WideNextCon.Owner is FamilyInstance) { using (Transaction trans = new Transaction(doc)) { trans.Start("MoveEndCreate"); //레듀셔와 패밀리 커넥터 끊기 WideCon.DisconnectFrom(WideNextCon); XYZ Vec = (narrowCon.Origin - WideCon.Origin).Normalize(); MoveElement(doc, WideCon.Owner, WideCon.Origin, Vec, Dist); // trans.Commit(); //} //MessageBox.Show("1"); //using (Transaction trans = new Transaction(doc)) //{ // trans.Start("CreatePipe"); Pipe newPipe = Pipe.Create(doc, PipeSystemTypeId, PipeTypeId, PipeLevelId, WideCon.Origin, WideNextCon.Origin); List NewPipeConLst = Util.GetElementConnectors(newPipe); NewPipeConLst.First().Radius = WideCon.Radius; doc.Regenerate(); //생성된 파이프의 커넥터들 연결 foreach (Connector con in NewPipeConLst) { if (con.Origin.IsAlmostEqualTo(WideCon.Origin, Unit.MMToFeet(0.1))) con.ConnectTo(WideCon); else if (con.Origin.IsAlmostEqualTo(WideNextCon.Origin, Unit.MMToFeet(0.1))) con.ConnectTo(WideNextCon); } trans.Commit(); } } else if (WideNextCon.Owner is Pipe) { Connector PipeOtherCon = Util.GetOtherConnector(WideNextCon); Connector NextFamilyCon = Util.GetNextElementConnector(PipeOtherCon); if (NextFamilyCon.Owner is FamilyInstance && NextFamilyCon != null) { Line pipeline = (PipeOtherCon.Owner.Location as LocationCurve).Curve as Line; double pipeDist = pipeline.GetEndPoint(0).DistanceTo(pipeline.GetEndPoint(1)); using (Transaction trans = new Transaction(doc)) { trans.Start("move"); WideCon.DisconnectFrom(WideNextCon); //간격 조정 거리보다 레듀셔에 붙은 파이프길이가 클 경우 if (pipeDist > Unit.MMToFeet(Dist)) { XYZ Vec = (NextFamilyCon.Origin - WideCon.Origin).Normalize(); //레듀셔 위치조정 MoveElement(doc, WideCon.Owner, WideCon.Origin, Vec, Math.Abs(Unit.FeetToMM(pipeDist) - Dist)); //파이프 위치조정 MoveElement(doc, WideNextCon.Owner, WideNextCon.Origin, Vec, Math.Abs(Unit.FeetToMM(pipeDist) - Dist)); } else { XYZ Vec = (WideCon.Origin - NextFamilyCon.Origin).Normalize(); //레듀셔 위치조정 MoveElement(doc, WideCon.Owner, WideCon.Origin, Vec, Math.Abs(Unit.FeetToMM(pipeDist) - Dist)); //파이프 위치조정 MoveElement(doc, WideNextCon.Owner, WideNextCon.Origin, Vec, Math.Abs(Unit.FeetToMM(pipeDist) - Dist)); } //커넥터 다시 잇기 WideCon.ConnectTo(WideNextCon); trans.Commit(); } } } } } } catch { } } //Element 커넥터에 붙어있는 객체를 구하는 함수 public void GetSideElement(Element Elem, ref List m_SideElements) { ConnectorSet ElemConSet = null; if (Elem is FamilyInstance) ElemConSet = (Elem as FamilyInstance).MEPModel.ConnectorManager.Connectors; else if (Elem is Pipe) ElemConSet = (Elem as Pipe).ConnectorManager.Connectors; else if (Elem is Duct) ElemConSet = (Elem as Duct).ConnectorManager.Connectors; foreach (Connector ElemCon in ElemConSet)//Tee 커넥터 { if (ElemCon.MEPSystem != null) { if (ElemCon.IsConnected == true) { //현재 커넥터에 연결된 다음 객체의 커넥터 구하기 Connector NextCon = GetNextElementConnector(ElemCon); if (NextCon == null) continue; if (NextCon.Owner is FamilyInstance) { bool tf = GetFamilyPartType(NextCon, PartType.Transition); if (tf)//레듀셔일 때 { //Pyosi(NextCon.Origin, 1); //MessageBox.Show("1"); //현재 커넥터의 객체가 커넥터가 2개일 때 현재 커넥터의 반대쪽 커넥터 구하기. Connector NextOtherCon1 = GetOtherConnector(NextCon); if (NextCon.Radius < NextOtherCon1.Radius) continue; FamilyInstance m_Reducer = NextCon.Owner as FamilyInstance; //레듀셔 옆 파이프 또는 덕트 커넥터 구하기 Connector NextOtherCon2 = GetNextElementConnector(NextOtherCon1); if (NextOtherCon2 == null) continue; //Tee와 레듀셔의 교차 커넥터 끊기 using (Transaction trans = new Transaction(doc)) { trans.Start("DisConnect"); ElemCon.DisconnectFrom(NextCon); trans.Commit(); } //Pyosi(NextCon.Origin, 1); //MessageBox.Show("1"); //Pyosi(NextOtherCon1.Origin, 1); //MessageBox.Show("2"); //Tee에 연결된 레듀셔의 커넥터에서 - 레듀셔 방향 XYZ VecPt = (NextOtherCon1.Origin - NextCon.Origin).Normalize(); //레듀셔 위치 조정 MoveElement(doc, NextCon.Owner, NextCon.Origin, VecPt, 200); if (NextOtherCon2.Owner is Pipe) { Pipe pipe = NextOtherCon2.Owner as Pipe; using (Transaction trans = new Transaction(doc)) { trans.Start("CreatePipe"); Level level = new FilteredElementCollector(doc).OfClass(typeof(Level)).First() as Level; Pipe newPipe = Pipe.Create(doc, pipe.GetTypeId(), level.Id, ElemCon, NextCon); trans.Commit(); } } } else { } } else if (NextCon.Owner is Pipe || NextCon.Owner is Duct) { //현재 커넥터의 객체가 커넥터가 2개일 때 현재 커넥터의 반대쪽 커넥터 구하기. Connector NextOtherCon1 = GetOtherConnector(NextCon); Pipe pipe = null; Duct duct = null; if (NextCon.Owner is Pipe) { //Pyosi(NextCon.Origin, 1); //MessageBox.Show("NextCon"); pipe = NextCon.Owner as Pipe; //파이프 옆 패밀리 커넥터 구하기 Connector familyCon = GetNextElementConnector(NextOtherCon1); if (familyCon == null) continue; //Pyosi(familyCon.Origin, 1); //MessageBox.Show("familyCon"); bool tf = GetFamilyPartType(familyCon, PartType.Transition); if (tf) { //현재 커넥터의 객체가 커넥터가 2개일 때 현재 커넥터의 반대쪽 커넥터 구하기. Connector familyOtherCon = GetOtherConnector(familyCon); if (familyCon.Radius < familyOtherCon.Radius) continue; //Pyosi(familyOtherCon.Origin, 1); //MessageBox.Show("familyOtherCon"); FamilyInstance m_Reducer = familyOtherCon.Owner as FamilyInstance; Connector NextOtherCon2 = GetNextElementConnector(familyOtherCon); if (NextOtherCon2 == null) continue; //Pyosi(NextOtherCon2.Origin, 1); //MessageBox.Show("NextOtherCon2"); XYZ VecPt = (NextCon.Origin - NextOtherCon1.Origin).Normalize(); double PipeDist = NextOtherCon1.Origin.DistanceTo(NextCon.Origin); //레듀셔 위치 조정 MoveElement(doc, familyCon.Owner, NextCon.Origin, VecPt, Unit.FeetToMM(PipeDist) - 200); if (NextOtherCon2.Owner is Pipe) { Pipe pipe2 = NextOtherCon2.Owner as Pipe; //NextOtherCon2.Origin } } } else if (NextCon.Owner is Duct) { duct = NextCon.Owner as Duct; //덕트 옆 패밀리 커넥터 구하기 Connector NextOtherCon2 = GetNextElementConnector(NextOtherCon1); } } //Pyosi(aa.Origin,1); //MessageBox.Show("1"); } } } } //객체 이동 함수 public void MoveElement(Autodesk.Revit.DB.Document doc, Element elem, XYZ BasePt, XYZ VecPt, double mm_MoveDist) { //polar점 구하기. XYZ plrPt = Util.Polar(BasePt, VecPt, Unit.MMToFeet(mm_MoveDist)); //커넥터 점과 Polar로 구한점을 빼 이동할 거리 만들기. XYZ NewPt = new XYZ(plrPt.X - BasePt.X, plrPt.Y - BasePt.Y, plrPt.Z - BasePt.Z); ElementTransformUtils.MoveElement(doc, elem.Id, NewPt); } //패밀리의 타입구분 public bool GetFamilyPartType(Connector baseCon, PartType partType) { if (baseCon.Owner is FamilyInstance) { FamilyInstance family = baseCon.Owner as FamilyInstance; Parameter param = family.Symbol.Family.get_Parameter(BuiltInParameter.FAMILY_CONTENT_PART_TYPE); if (partType == (PartType)param.AsInteger()) return true; else return false; } return false; } //객체의 다른 커넥터 구하기.(객체의 커넥터가 2개일 때.) public Connector GetOtherConnector(Connector baseCon) { Connector resCon = null; ConnectorSet ConSet = null; MEPSystem mepSystem = baseCon.MEPSystem; FamilyInstance family = null; Pipe pipe = null; Duct duct = null; if (mepSystem != null) { if (baseCon.IsConnected == true) { if (baseCon.Owner is FamilyInstance) { family = baseCon.Owner as FamilyInstance; ConSet = family.MEPModel.ConnectorManager.Connectors; } else if (baseCon.Owner is Pipe) { pipe = baseCon.Owner as Pipe; ConSet = pipe.ConnectorManager.Connectors; } else if (baseCon.Owner is Duct) { duct = baseCon.Owner as Duct; ConSet = duct.ConnectorManager.Connectors; } foreach (Connector con in ConSet) { //입력 커넥터와 같은 커넥터는 넘긴다, if (con.Origin.IsAlmostEqualTo(baseCon.Origin, Unit.MMToFeet(0.001))) { //Pyosi(con.Origin, 1); //MessageBox.Show("1"); continue; } else { //Pyosi(con.Origin, 1); //MessageBox.Show("2"); resCon = con; } } } } //Pyosi(resCon.Origin, 1); //MessageBox.Show("2"); return resCon; } //커넥터에 연결된 다음 객체의 커넥터 구하기. public Connector GetNextElementConnector(Connector baseCon) { Connector resCon = null; MEPSystem mepSystem = baseCon.MEPSystem; if (mepSystem != null) { if (baseCon.IsConnected == true) { ConnectorSet ConSet = baseCon.AllRefs; ConnectorSetIterator csi = ConSet.ForwardIterator(); while (csi.MoveNext()) { Connector connected = csi.Current as Connector; if (connected != null && connected.ConnectorType == ConnectorType.End) { if (baseCon.Origin.IsAlmostEqualTo(connected.Origin, Unit.MMToFeet(0.001))) { if (connected.IsConnected == true) { ConnectorSet ConnectedSet = null; FamilyInstance family = null; Pipe pipe = null; Duct duct = null; if (connected.Owner is FamilyInstance) { family = connected.Owner as FamilyInstance; ConnectedSet = family.MEPModel.ConnectorManager.Connectors; } else if (connected.Owner is Pipe) { pipe = connected.Owner as Pipe; ConnectedSet = pipe.ConnectorManager.Connectors; } else if (connected.Owner is Duct) { duct = connected.Owner as Duct; ConnectedSet = duct.ConnectorManager.Connectors; } foreach (Connector NextCon in ConnectedSet) { if (connected.Origin.IsAlmostEqualTo(NextCon.Origin, Unit.MMToFeet(0.001))) { resCon = NextCon; } } } } } }//while end } } //Pyosi(resCon.Origin, 1); //MessageBox.Show("1"); return resCon; } } }