using System; using Autodesk.Revit.UI; using Autodesk.Revit.DB; using System.Collections.Generic; using System.Windows.Forms; using System.Linq; using Autodesk.Revit.UI.Selection; using Autodesk.Revit.DB.Plumbing; using KDCS.Utils; using Autodesk.Revit.DB.Structure; using KMBIM.Revit.Tools.Cmd.SprinklerConnect; namespace KMBIM { [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] class PipeTrimSpacing : IExternalCommand { UIApplication uiapp; UIDocument uidoc; Autodesk.Revit.DB.Document doc; Autodesk.Revit.Creation.Application creApp; Autodesk.Revit.Creation.Document creDoc; Autodesk.Revit.UI.ExternalCommandData m_commandData; public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { m_commandData = commandData; uiapp = commandData.Application; uidoc = uiapp.ActiveUIDocument; doc = uidoc.Document; creApp = uiapp.Application.Create; creDoc = doc.Create; double m_TrimDist = 0; List m_ElemLst = new List(); List m_ElemIdLst = new List(); try { IList pickrefs = commandData.Application.ActiveUIDocument.Selection.PickObjects(Autodesk.Revit.UI.Selection.ObjectType.Element, new SelectionFilter(), "절단할 배관 선택 : "); if (pickrefs == null) return Result.Cancelled; foreach (Reference refer in pickrefs) { ElementId id = refer.ElementId; m_ElemIdLst.Add(id); Element elem = doc.GetElement(id); m_ElemLst.Add(elem); } Form_TrimSpacing dlg = new Form_TrimSpacing(); //라벨 m / 길이 : 6 변경 dlg.m_label = "m"; dlg.m_trimDist = 6; dlg.ShowDialog(); if (dlg.DialogResult == DialogResult.Cancel) return Result.Cancelled; //대화상자 값 가져오기 m_TrimDist = Math.Round(dlg.m_trimDist); while (m_ElemIdLst.Count > 0) { //관경이 가장 큰 파이프 Pipe BigDiaPipe = GetMaxDiaPipe(m_ElemIdLst); //말단 파이프 커넥터 찾기 Connector StartPipeCon = FindFirstpipeConnector(BigDiaPipe); Util.Pyosi(doc, StartPipeCon.Origin, 1); MessageBox.Show("1"); SetPipe(StartPipeCon, m_TrimDist, ref m_ElemIdLst); break; }//while end //파이프 중 가장 큰 관경 파이프 찾기 //Pipe pp = GetMaxDiaPipe(m_ElemIdLst); //관경이 가장 큰 파이프의 말단 찾기 //Connector con = FindFirstpipeConnector(pp); //Connector OtherCon = Util.GetOtherConnector(con); //XYZ vec = (OtherCon.Origin - con.Origin).Normalize(); //XYZ Revec = (con.Origin - OtherCon.Origin).Normalize(); //return Result.Succeeded; //if (con != null) //{ // //using(Transaction trans = new Transaction(doc)) // //{ // // trans.Start("fitting"); // Connector ttt = FittingProcess(con, m_TrimDist, m_ElemIdLst, vec, Revec); // // trans.Commit(); // //} //} int NotDrawCnt = 0; //if (NotDrawCnt > 0) // MessageBox.Show("작도 실패 : " + NotDrawCnt + " 개", "결과"); } catch (Exception e) { MessageBox.Show("" + e); } return Result.Succeeded; } //관경이 가장 큰 파이프 찾기 private Pipe GetMaxDiaPipe(List lst) { double dMaxDia = 0; Pipe maxDiaPipe = null; foreach (ElementId id in lst) { Pipe pp = doc.GetElement(id) as Pipe; if (pp.Diameter > dMaxDia) { dMaxDia = pp.Diameter; maxDiaPipe = pp; } } return maxDiaPipe; } //파이프 리스트 중 값이 있으면 리스트에서 제거 public void SetPipe(Connector startPipeCon, double TrimDist, ref List ElemIdLst) { List CuttingPipeLst = new List(); //파이프 엔드 커넥터 Connector endPipeCon = Util.GetOtherConnector(startPipeCon); //기준 파이프의 끝-시작 방향 벡터 XYZ vec = (endPipeCon.Origin - startPipeCon.Origin).Normalize(); XYZ reVec = (startPipeCon.Origin - endPipeCon.Origin).Normalize(); //파이프 시작점쪽으로 객체 끝점 구하기. Connector loopCon = endPipeCon; while (true) { Connector nextCon = GetConnected(loopCon, vec, reVec); if (nextCon == null) { break; } if (nextCon.Owner is FamilyInstance) { //엘보일 때 vec reVec 방향변경 if (Util.GetFamilyPartType(nextCon, PartType.Elbow)) { Connector NextOtherCon = GetNextElementOtherConnector(nextCon); if (NextOtherCon != null) { if (NextOtherCon.Owner is FamilyInstance) { Connector FamOtherCon = GetNextElementOtherConnector(nextCon); vec = (nextCon.Origin - FamOtherCon.Origin).Normalize(); reVec = (FamOtherCon.Origin - nextCon.Origin).Normalize(); } else if (NextOtherCon.Owner is Pipe) { vec = (nextCon.Origin - NextOtherCon.Origin).Normalize(); reVec = (NextOtherCon.Origin - nextCon.Origin).Normalize(); } } } else if (Util.GetFamilyPartType(nextCon, PartType.Cap)) { break; } } else if (nextCon.Owner is Pipe) { //파이프 리스트에 있는 파이프면 따로 담아둠. foreach(ElementId id in ElemIdLst) { if(nextCon.Owner.Id == id) { CuttingPipeLst.Add(id); ElemIdLst.Remove(id); break; } } //Connector NextOtherCon = Util.GetOtherConnector(nextCon); if (nextCon != null) { //파이프가 연결된 객체가 없으면 종료 if (nextCon.IsConnected == false) { break; } } } loopCon = nextCon; }//while end MessageBox.Show("" + CuttingPipeLst.Count()); } /// /// 유니온 피팅 루틴 /// /// 1본 따기 할 시작커넥터 /// 절단길이 public void FittingStart(Connector baseCon, double m_TrimDist) { int NotDrawCnt = 0; using (Transaction trans = new Transaction(doc)) { trans.Start("1"); List m_DivPtLst = new List(); XYZ sp = null, ep = null; XYZ FlowVec = null; Util.GetStartEndPoint(baseCon.Owner as Pipe, ref sp, ref ep); if (ep.IsAlmostEqualTo(baseCon.Origin, Unit.MMToFeet(0.01))) { XYZ passPt = ep; ep = sp; sp = passPt; } Line pipeline = ((baseCon.Owner as Pipe).Location as LocationCurve).Curve as Line; FlowVec = (ep - sp).Normalize(); //XYZ trimPt = Util.Polar(sp, FlowVec, Unit.MMToFeet(m_TrimDist * 1000)); XYZ trimPt = Util.Polar(sp, FlowVec, Unit.MMToFeet(m_TrimDist * 1000)); int Cnt = 1; while (true) { IntersectionResult interRes = pipeline.Project(trimPt); XYZ interResPt = interRes.XYZPoint; //Util.Pyosi(doc, interResPt, 1); //MessageBox.Show("1"); //interRes가 선의 끝점부터 if (interResPt.IsAlmostEqualTo(trimPt, Unit.MMToFeet(0.01))) { m_DivPtLst.Add(interResPt); //피팅 Connector resCon = FittingUnion(baseCon.Owner, interResPt, m_TrimDist, ref NotDrawCnt); if (resCon == null) break; //trimPt = Util.Polar(resCon.Origin, FlowVec, Unit.MMToFeet(m_TrimDist * 1000)); trimPt = Util.Polar(resCon.Origin, FlowVec, Unit.MMToFeet(m_TrimDist * 1000)); } else break; Cnt++; }//while end //foreach end trans.Commit(); } } /// /// 커넥터부터 시작하여 선택한 객체만 유니온 부착 /// /// 파이프 말단 커넥터 /// 절단 길이 /// 선택한 객체ID 리스트 /// public Connector FittingProcess(Connector StCon,double m_TrimDist, List ElemIdList, XYZ DirVec, XYZ ReVec) { Connector resCon = null; //Util.GetStartEndConnector(StCon.Owner as Pipe, ref SpCon, ref EpCon); Connector EtCon = Util.GetOtherConnector(StCon); //기준 파이프의 끝-시작 방향 벡터 //XYZ vec = (EtCon.Origin - StCon.Origin).Normalize(); //XYZ reVec = (StCon.Origin - EtCon.Origin).Normalize(); XYZ vec = DirVec; XYZ reVec = ReVec; //파이프 시작점쪽으로 객체 끝점 구하기. Connector loopCon = EtCon; while (true) { Connector nextCon = loopCon; if (nextCon == null) { resCon = null; break; } if (nextCon.Owner is FamilyInstance) { //엘보일 때 vec reVec 방향변경 if (Util.GetFamilyPartType(nextCon, PartType.Elbow)) { Connector NextOtherCon = GetNextElementOtherConnector(nextCon); if (NextOtherCon != null) { if (NextOtherCon.Owner is FamilyInstance) { Connector FamOtherCon = GetNextElementOtherConnector(nextCon); vec = (nextCon.Origin - FamOtherCon.Origin).Normalize(); reVec = (FamOtherCon.Origin - nextCon.Origin).Normalize(); } else if (NextOtherCon.Owner is Pipe) { vec = (nextCon.Origin - NextOtherCon.Origin).Normalize(); reVec = (NextOtherCon.Origin - nextCon.Origin).Normalize(); } } } else if (Util.GetFamilyPartType(nextCon, PartType.Cap)) { //Util.Pyosi(doc, nextCon.Origin,1); //MessageBox.Show("1"); //캡일 때 종료 resCon = null; break; }//티 또는 크로스일 때 분기 파이프에서 FittingProcess()함수 돌리기 else if (Util.GetFamilyPartType(nextCon, PartType.Tee) || (Util.GetFamilyPartType(nextCon, PartType.Cross))) { List BranchConLst = GetBranchConnector(nextCon, vec, reVec); foreach(Connector con in BranchConLst) { Connector TeeNextCon = Util.GetNextElementConnector(con); if (TeeNextCon == null) continue; if(TeeNextCon.Owner is Pipe) { Connector pipeOtherCon = Util.GetOtherConnector(TeeNextCon); vec = (pipeOtherCon.Origin - con.Origin).Normalize(); reVec = (con.Origin - pipeOtherCon.Origin).Normalize(); resCon = FittingProcess(TeeNextCon, m_TrimDist, ElemIdList, vec, reVec); if (resCon == null) break; } else if(TeeNextCon.Owner is FamilyInstance) { Connector famOtherCon = Util.GetOtherConnector(TeeNextCon); Connector famNextCon = Util.GetNextElementConnector(famOtherCon); Connector pipeOtherCon = Util.GetOtherConnector(famNextCon); if (famNextCon == null) continue; vec = (pipeOtherCon.Origin - famNextCon.Origin).Normalize(); reVec = (famNextCon.Origin - pipeOtherCon.Origin).Normalize(); resCon = FittingProcess(famNextCon, m_TrimDist, ElemIdList, vec, reVec); if (resCon == null) break; } } //if(resCon == null)break; //Connector pipeCon = Util.GetNextElementConnector(BranchCon); //Connector pipeOtherCon = Util.GetOtherConnector(pipeCon); //FittingProcess(pipeCon, m_TrimDist); } else { Connector nextOtherCon = Util.GetOtherConnector(nextCon); List famConLst = Util.GetElementConnectors(nextCon.Owner); if (nextOtherCon == null) { resCon = null; break; } else { if (nextOtherCon.IsConnected == false) { resCon = null; break; } if (famConLst.Count < 2) { resCon = null; break; } } } } else if (nextCon.Owner is Pipe) { Util.Pyosi(doc, nextCon.Origin, 1); MessageBox.Show("1"); bool containPipe = ElemIdList.Contains(nextCon.Owner.Id); if (containPipe == true) { Connector NextOtherCon = Util.GetOtherConnector(nextCon); if (NextOtherCon != null) { FittingStart(NextOtherCon, m_TrimDist); //Util.Pyosi(doc, NextOtherCon.Origin, 1); //MessageBox.Show("1"); } } Connector Next2Con = Util.GetNextElementConnector(nextCon); if (Next2Con == null) { resCon = null; break; } } loopCon = GetConnected(nextCon, vec, reVec); }//while end return resCon; } /// /// 말단 파이프 커넥터 구하는 루틴 /// /// 관경이 가장 큰 파이프 /// public Connector FindFirstpipeConnector(Pipe basePipe) { Connector SpCon = null, EpCon = null, resCon = null; Util.GetStartEndConnector(basePipe, ref SpCon, ref EpCon); //기준 파이프의 끝-시작 방향 벡터 XYZ vec = (EpCon.Origin - SpCon.Origin).Normalize(); XYZ reVec = (SpCon.Origin - EpCon.Origin).Normalize(); //파이프 시작점쪽으로 객체 끝점 구하기. Connector loopCon = SpCon; while (true) { Connector nextCon = GetConnected(loopCon, vec, reVec); if (nextCon == null) { resCon = loopCon; break; } if (nextCon.Owner is FamilyInstance) { //엘보일 때 vec reVec 방향변경 if (Util.GetFamilyPartType(nextCon, PartType.Elbow)) { Connector NextOtherCon = GetNextElementOtherConnector(nextCon); if (NextOtherCon != null) { if (NextOtherCon.Owner is FamilyInstance) { Connector FamOtherCon = GetNextElementOtherConnector(nextCon); vec = (nextCon.Origin - FamOtherCon.Origin).Normalize(); reVec = (FamOtherCon.Origin - nextCon.Origin).Normalize(); } else if (NextOtherCon.Owner is Pipe) { vec = (nextCon.Origin - NextOtherCon.Origin).Normalize(); reVec = (NextOtherCon.Origin - nextCon.Origin).Normalize(); } } } else if (Util.GetFamilyPartType(nextCon, PartType.Cap)) { //Util.Pyosi(doc, nextCon.Origin,1); //MessageBox.Show("1"); //캡일 때 종료 resCon = loopCon; break; } } else if (nextCon.Owner is Pipe) { //Connector NextOtherCon = Util.GetOtherConnector(nextCon); if (nextCon != null) { //파이프가 연결된 객체가 없으면 종료 if (nextCon.IsConnected == false) { resCon = nextCon; break; } } } loopCon = nextCon; }//while end return resCon; } /// /// 커넥터에 붙어있는 객체의 반대쪽 커넥터 구하기 /// /// 기준 커넥터 /// public Connector GetNextElementOtherConnector(Connector baseCon) { Connector resCon = null; Connector NextCon = Util.GetNextElementConnector(baseCon); if (NextCon != null) { Connector NextOtherCon = Util.GetOtherConnector(NextCon); if (NextOtherCon != null) resCon = NextOtherCon; } return resCon; } /// /// Tee,Cross 가지 분기 구하기 /// /// 기준 커넥터 /// Tee 또는 cross에 붙은 파이프의 (끝-시작)벡터 /// Tee 또는 cross에 붙은 파이프의 (시작-끝)벡터 /// public List GetBranchConnector(Connector baseCon,XYZ vec,XYZ reVec) { List resConLst = new List(); List famLst = Util.GetElementConnectors(baseCon.Owner); foreach(Connector con in famLst) { if (baseCon.Origin.IsAlmostEqualTo(con.Origin, Unit.MMToFeet(0.01))) continue; XYZ CurVec = (con.Origin - baseCon.Origin).Normalize(); if ((CurVec.IsAlmostEqualTo(vec, Unit.MMToFeet(0.01)) || CurVec.IsAlmostEqualTo(reVec, Unit.MMToFeet(0.01)))) continue; else { resConLst.Add(con); } } return resConLst; } /// /// /// /// 기준 커넥터 /// 진행방향 /// 진행역방향 /// public Connector GetConnected(Connector BaseCon, XYZ FlowVec, XYZ ReverseVec) { Connector resCon = null; //기본 커넥터 옆에 객체 구하기 Connector NextCon = Util.GetNextElementConnector(BaseCon); if (NextCon == null) return null; if (NextCon.Owner is FamilyInstance) { if (Util.GetFamilyPartType(NextCon, PartType.Tee) == true || Util.GetFamilyPartType(NextCon, PartType.Cross) == true) { Connector NextOtherCon = GetOtherConnector(NextCon, FlowVec, ReverseVec); if (NextOtherCon != null) resCon = NextOtherCon; else resCon = NextCon; } else//엘보 레듀셔 등 { Connector NextOtherCon = Util.GetOtherConnector(NextCon); if (NextOtherCon != null) { resCon = NextOtherCon; } else///커넥터가 1개이면 { resCon = NextCon; } } } else if (NextCon.Owner is Pipe) { Connector NextOtherCon = Util.GetOtherConnector(NextCon); if (NextOtherCon != null) resCon = NextOtherCon; } return resCon; } //NextCon 객체의 정방향 또는 역방향에 위치한 커넥터 구하기. public Connector GetOtherConnector(Connector baseCon, XYZ FlowVec, XYZ ReverseVec) { Connector resCon = null; ConnectorSet ConSet = null; MEPSystem mepSystem = baseCon.MEPSystem; FamilyInstance family = null; Pipe pipe = 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; } foreach (Connector con in ConSet) { //입력 커넥터와 같은 커넥터는 넘긴다, if (con.Origin.IsAlmostEqualTo(baseCon.Origin, Unit.MMToFeet(0.001))) continue; else { XYZ CurVec = (con.Origin - baseCon.Origin).Normalize(); //정방향 또는 역방향 커넥터를 찾아 리턴 if (CurVec.IsAlmostEqualTo(FlowVec, Unit.MMToFeet(0.01)) || CurVec.IsAlmostEqualTo(ReverseVec, Unit.MMToFeet(0.01))) resCon = con; } } } } return resCon; } /// /// 유니온 피팅 /// /// 파이프 /// 자를 점 위치 /// 1본 길이 /// 유니온 피팅 실패 갯수 /// public Connector FittingUnion(Element elem, XYZ divPt, double TrimDist, ref int NotDrawCnt) { Connector resCon = null; try { Pipe mainPipe = elem as Pipe; Connector mainspCon = null, mainepCon = null; Util.GetStartEndConnector(mainPipe, ref mainspCon, ref mainepCon); if (divPt.DistanceTo(mainspCon.Origin) < mainspCon.Radius * 3 || divPt.DistanceTo(mainepCon.Origin) < mainspCon.Radius * 3) { return null; } //파이프 자르기 List ElemIDLst = Util.DivideElement(doc, mainPipe, divPt); if (ElemIDLst != null) { //List famconlst = Util.GetElementConnectors(newFamily); List pipe1conlst = Util.GetElementConnectors(doc.GetElement(ElemIDLst.First())); List pipe2conlst = Util.GetElementConnectors(doc.GetElement(ElemIDLst[1])); if (pipe1conlst[0].Origin.IsAlmostEqualTo(pipe1conlst[1].Origin, Unit.MMToFeet(0.01)) || pipe2conlst[0].Origin.IsAlmostEqualTo(pipe2conlst[1].Origin, Unit.MMToFeet(0.01))) { NotDrawCnt++; return null; } //자른 객체의 겹치는 커넥터에 유니온 피팅 foreach (Connector con1 in pipe1conlst) { foreach (Connector con2 in pipe2conlst) { if (con1.Origin.IsAlmostEqualTo(con2.Origin, Unit.MMToFeet(0.01))) { //유니온 피팅 FamilyInstance famUnion = doc.Create.NewUnionFitting(con1, con2); if (famUnion != null) { Transform familyTrans = famUnion.GetTransform(); List famConLst = Util.GetElementConnectors(famUnion); doc.Regenerate(); Connector Fsp = null, Fep = null, Ssp = null, Sep = null; Util.GetStartEndConnector(doc.GetElement(ElemIDLst.First()) as Pipe, ref Fsp, ref Fep); Util.GetStartEndConnector(doc.GetElement(ElemIDLst[1]) as Pipe, ref Ssp, ref Sep); //유니온 장착 후 앞쪽 파이프 길이가 지정한 길이가 아니면 유니온 옮김 //if (Math.Abs(Fsp.Origin.DistanceTo(Fep.Origin) - Unit.MMToFeet(TrimDist * 1000)) > Unit.MMToFeet(0.01)) if (Math.Abs(Fsp.Origin.DistanceTo(Fep.Origin) - Unit.MMToFeet(TrimDist * 1000)) > Unit.MMToFeet(0.01)) { if (Fsp.Origin.DistanceTo(Fep.Origin) - Unit.MMToFeet(TrimDist * 1000) < 0) { /*유니온 두개의 커넥터 사이기 길이 반만큼 이동해야 하기 때문에 뒤쪽 파이프 시작점으로 이동*/ XYZ translation = Ssp.Origin - familyTrans.Origin; ElementTransformUtils.MoveElement(doc, famUnion.Id, translation); doc.Regenerate(); //위치 변경 후 뒤쪽 파이프 시작점 리턴(시작점부터 다시 지정값만큼 이동해야 해서) Util.GetStartEndConnector(doc.GetElement(ElemIDLst[1]) as Pipe, ref Ssp, ref Sep); //절단 길이 + 유니온 이동거리 > 남은 파이프 길이 = return //if (Unit.MMToFeet(TrimDist * 1000) + (Ssp.Origin.DistanceTo(familyTrans.Origin)*3) // > Ssp.Origin.DistanceTo(Sep.Origin)) return null; resCon = Ssp; break; } } } else NotDrawCnt++; break; } } } } } catch { return resCon; } return resCon; } public class SelectionFilter : ISelectionFilter { public bool AllowElement(Element element) { if (element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeCurves) { return true; } return false; } public bool AllowReference(Reference refer, XYZ point) { return false; } } } }