using Autodesk.Revit.ApplicationServices; using Autodesk.Revit.Creation; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Plumbing; using Autodesk.Revit.DB.Structure; using Autodesk.Revit.UI; using Autodesk.Revit.UI.Selection; using KDCS.Utils; using KMBIM.Revit.Tools; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using View = Autodesk.Revit.DB.View; namespace KMBIM { [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] public class DrainConnect : 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; try { if (!WorkMain.GetInstance().IsValid) return Autodesk.Revit.UI.Result.Succeeded; List WyeFamSymLst = new List(); //콤보박스에 뿌릴 Y 패밀리 심볼 리스트 구하기 ICollection WyeCollector = new FilteredElementCollector(doc).OfClass(typeof(FamilySymbol)).ToElements(); foreach (Element e in WyeCollector) { FamilySymbol FamSym = e as FamilySymbol; if (FamSym == null) continue; bool b_Wye = Util.GetFamilyPartType(FamSym, PartType.Wye); if (b_Wye) { WyeFamSymLst.Add(e as FamilySymbol); } } Form_DrainConnect dlg = new Form_DrainConnect(); dlg.YFamSymLst = WyeFamSymLst; dlg.document = doc; if (dlg.ShowDialog() == DialogResult.Cancel) return Result.Cancelled; int Connect_Type = dlg.Rad_Idx; double m_D1 = dlg.m_D1; string ComboFamName = dlg.m_YFamName; FamilySymbol YFamSymbol = null; foreach (FamilySymbol sym in WyeFamSymLst) { if (ComboFamName == sym.FamilyName) YFamSymbol = sym; } //부착할 메인 배관 선택 Reference pickRef = commandData.Application.ActiveUIDocument.Selection.PickObject (ObjectType.Element, new PipeSelectionFilter(), "배수관 연결할 메인관 선택 : "); Pipe pickPipe = doc.GetElement(pickRef) as Pipe; //위생기기 선택 IList pickRefs = commandData.Application.ActiveUIDocument.Selection.PickObjects (ObjectType.Element, new HygienicSelectionFilter(), "위생기기 선택 : "); //패밀리 리스트로 저장 List FamLst = new List(); foreach (Reference refer in pickRefs) { Element elem = doc.GetElement(refer); if (elem is FamilyInstance) FamLst.Add(elem as FamilyInstance); } //패밀리 중 가장 아래쪽 커넥터 리스트 저장 List bottomConLst = new List(); foreach (FamilyInstance fam in FamLst) { Connector bottomCon = GetBottonConnector(fam); if (bottomCon != null) bottomConLst.Add(bottomCon); } ////메인관 offset 값 > 가장 아래쪽 커넥터 + 65mm 면 오류메시지 후 종료 //foreach (Connector con in bottomConLst) //{ // //65mm 는 관경100 배관 엘보 길이 // if (pickPipe.LevelOffset > con.Origin.Z - Unit.MMToFeet(65)) // { // MessageBox.Show("연결할 메인배관과 위생기구 사이의 거리가 좁습니다.", "오류"); // return Result.Cancelled; // } //} XYZ DirPt = pickRef.GlobalPoint; XYZ sp = null, ep = null; Util.GetStartEndPoint(doc.GetElement(pickRef) as Pipe, ref sp, ref ep); XYZ dirpt = (sp - ep).Normalize(); //T,YT에 따른 작업 if (Connect_Type == 0) TeeType1Process(pickPipe, DirPt, bottomConLst); else if (Connect_Type == 1) TeeType2Process(pickPipe, dirpt, m_D1, bottomConLst); else if (Connect_Type == 2) YTeeType1Process(pickPipe, m_D1, DirPt, bottomConLst, YFamSymbol); else if (Connect_Type == 3) YTeeType2Process(pickPipe, m_D1, DirPt, bottomConLst, YFamSymbol); } catch (Exception e) { //MessageBox.Show("" + e); } return Result.Succeeded; } /// /// YTee 타입1 연결 프로세스 /// /// 메인관 /// 위생기기 가장 아래쪽 커넥터 void TeeType1Process(Pipe mainPipe, XYZ Dirpt, List bottomConLst) { ElementId PipeSystemTypeId = mainPipe.MEPSystem.GetTypeId(); ElementId PipeTypeId = mainPipe.PipeType.Id; ElementId ViewId = mainPipe.ReferenceLevel.Id; int idx_Error1 = 0, idx_Error2 = 0, idx_Error3 = 0; XYZ mainSp = null, mainEp = null; Util.GetStartEndPoint(mainPipe, ref mainSp, ref mainEp); bool isDir = false; //중간점에서 if (Dirpt.DistanceTo(mainEp) < Dirpt.DistanceTo(mainSp)) isDir = true; try { using (Transaction trans = new Transaction(doc)) { trans.Start("t"); List pts = new List(); //foreach (Connector con in bottomConLst) for (int i = 0; i < bottomConLst.Count; i++) { pts.Clear(); Connector con = bottomConLst[i]; Line mainLine = (mainPipe.Location as LocationCurve).Curve as Line; IntersectionResult interRes = mainLine.Project(con.Origin); if (interRes == null) continue; //메인과 커넥터 교차점 XYZ MainInterPt = interRes.XYZPoint; //위생기기 커넥터와 메인배관 가까운 점이 메인배관의 시작 또는 끝 점 일 경우 if (MainInterPt.IsAlmostEqualTo(mainSp) || MainInterPt.IsAlmostEqualTo(mainEp)) { idx_Error1++; continue; } //위생기기 커넥터 높이가 메인배관 교차점+여유길이보다 같거나 낮을 때 if (MainInterPt.Z + Unit.MMToFeet(79) > con.Origin.Z) { idx_Error2++; continue; } XYZ pipeEpt = Util.Polar(con.Origin, con.Origin, new XYZ(con.Origin.X, con.Origin.Y, con.Origin.Z - Unit.MMToFeet(1)), Math.Abs(MainInterPt.Z - con.Origin.Z)); //Util.Pyosi(doc, pipeEpt, 0); //Util.Pyosi(doc, MainInterPt, 0); if (pipeEpt.DistanceTo(MainInterPt) < Unit.MMToFeet(140)) { idx_Error3++; continue; } Parameter paramMainDia = mainPipe.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM); pts.Add(con.Origin); pts.Add(pipeEpt); pts.Add(MainInterPt); //배관 생성 List pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId); //T연결 Pipe LastPipe = pipeLst.Last(); Connector spCon = null, epCon = null; Util.GetStartEndConnector(LastPipe, ref spCon, ref epCon); List divLst = Util.DivideElement(doc, mainPipe, epCon.Origin); doc.Regenerate(); List crossConLst = new List(); foreach (ElementId id in divLst) { //Pipe divPipe = doc.GetElement(id) as Pipe; List ConLst = Util.GetElementConnectors(doc.GetElement(id)); foreach (Connector DivCon in ConLst) { if (epCon.Origin.IsAlmostEqualTo(DivCon.Origin)) crossConLst.Add(DivCon); } } FamilyInstance tee = null; if (isDir == true) tee = doc.Create.NewTeeFitting(crossConLst.Last(), crossConLst.First(), epCon); else tee = doc.Create.NewTeeFitting(crossConLst.First(), crossConLst.Last(), epCon); doc.Regenerate(); //자른 파이프로 변경 mainPipe = doc.GetElement(divLst.First()) as Pipe; //자른 파이프가 다음 커넥터와 교차점이 없으면 다음 자른파이프로 변경 if (bottomConLst.Count > i + 1) { Line divLine = (mainPipe.Location as LocationCurve).Curve as Line; Connector cons = bottomConLst[i + 1]; IntersectionResult intersection = divLine.Project(cons.Origin); if (intersection != null) { XYZ interpt = intersection.XYZPoint; List divMainConLst = Util.GetElementConnectors(mainPipe); foreach (Connector mainCon in divMainConLst) { if (interpt.IsAlmostEqualTo(mainCon.Origin)) { mainPipe = doc.GetElement(divLst.Last()) as Pipe; break; } } } } } trans.Commit(); string error_str = ""; if (idx_Error1 != 0) error_str += idx_Error1 + "개의 위생기기와 메인배관이 교차하지 않습니다."; if (idx_Error2 != 0) error_str += "\n" + idx_Error2 + "개의 위생기기와 배관사이 연결할 높이가 좁습니다."; if (idx_Error3 != 0) error_str += "\n" + idx_Error3 + "개의 위생기기와 배관사이 연결할 거리가 좁습니다."; if (error_str != "") MessageBox.Show(error_str, "오류"); } } catch (Exception e) { // MessageBox.Show("" + e); } } /// /// YTee 타입2 연결 프로세스 /// /// 메인관 /// 위생기기 가장 아래쪽 커넥터 void TeeType2Process(Pipe mainPipe, XYZ Dirpt, double dist_D1, List bottomConLst) { ElementId PipeSystemTypeId = mainPipe.MEPSystem.GetTypeId(); ElementId PipeTypeId = mainPipe.PipeType.Id; ElementId ViewId = mainPipe.ReferenceLevel.Id; int idx_Error1 = 0, idx_Error2 = 0; XYZ mainSp = null, mainEp = null; Util.GetStartEndPoint(mainPipe, ref mainSp, ref mainEp); bool isDir = false; //중간점에서 if (Dirpt.DistanceTo(mainEp) < Dirpt.DistanceTo(mainSp)) isDir = true; try { using (Transaction trans = new Transaction(doc)) { trans.Start("t"); List pts = new List(); //foreach (Connector con in bottomConLst) for (int i = 0; i < bottomConLst.Count; i++) { pts.Clear(); Connector con = bottomConLst[i]; Line mainLine = (mainPipe.Location as LocationCurve).Curve as Line; IntersectionResult interRes = mainLine.Project(con.Origin); if (interRes == null) continue; //메인과 커넥터 교차점 XYZ MainInterPt = interRes.XYZPoint; //D1 길이만큼 아래로 이동한 점 XYZ dist1pt = new XYZ(con.Origin.X, con.Origin.Y, con.Origin.Z - Unit.MMToFeet(1)); XYZ pipeD1pt = Util.Polar(con.Origin, con.Origin, dist1pt, Unit.MMToFeet(dist_D1)); XYZ pipeMainZpt = Util.Polar(con.Origin, con.Origin, pipeD1pt, con.Origin.Z - MainInterPt.Z); //Util.Pyosi(doc, pipeMainZpt, 0); //메인관 교차점과 D1pt 벡터 방향 XYZ interZD1pt = new XYZ(MainInterPt.X, MainInterPt.Y, pipeD1pt.Z); XYZ vec = (interZD1pt - pipeD1pt).Normalize(); // D1+엘보 여유분이 위생기기-메인관 거리보다 클 경우 if (con.Origin.DistanceTo(pipeMainZpt) <= Unit.MMToFeet(dist_D1 + 100)) { idx_Error1++; //MessageBox.Show("위생기기와 배관사이 연결할 거리가 좁습니다.", "오류"); continue; } // 45도 절단점이 메인관 시작 또는 끝점일 경우 작업 취소 if (MainInterPt.IsAlmostEqualTo(mainSp) || MainInterPt.IsAlmostEqualTo(mainEp)) { idx_Error2++; continue; } pts.Add(con.Origin); pts.Add(pipeD1pt); pts.Add(interZD1pt); pts.Add(MainInterPt); //파이프 생성 List pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId); //T연결 Pipe LastPipe = pipeLst.Last(); Connector spCon = null, epCon = null; Util.GetStartEndConnector(LastPipe, ref spCon, ref epCon); List divLst = Util.DivideElement(doc, mainPipe, epCon.Origin); doc.Regenerate(); List crossConLst = new List(); foreach (ElementId id in divLst) { //Pipe divPipe = doc.GetElement(id) as Pipe; List ConLst = Util.GetElementConnectors(doc.GetElement(id)); foreach (Connector DivCon in ConLst) { if (epCon.Origin.IsAlmostEqualTo(DivCon.Origin)) crossConLst.Add(DivCon); } } FamilyInstance tee = null; if (isDir == true) tee = doc.Create.NewTeeFitting(crossConLst.Last(), crossConLst.First(), epCon); else tee = doc.Create.NewTeeFitting(crossConLst.First(), crossConLst.Last(), epCon); doc.Regenerate(); //자른 파이프로 변경 mainPipe = doc.GetElement(divLst.First()) as Pipe; //자른 파이프가 다음 커넥터와 교차점이 없으면 다음 자른파이프로 변경 if (bottomConLst.Count > i + 1) { Line divLine = (mainPipe.Location as LocationCurve).Curve as Line; Connector cons = bottomConLst[i + 1]; IntersectionResult intersection = divLine.Project(cons.Origin); if (intersection != null) { XYZ interpt = intersection.XYZPoint; List divMainConLst = Util.GetElementConnectors(mainPipe); foreach (Connector mainCon in divMainConLst) { if (interpt.IsAlmostEqualTo(mainCon.Origin)) { mainPipe = doc.GetElement(divLst.Last()) as Pipe; break; } } } } } trans.Commit(); string error_str = ""; if (idx_Error1 != 0) error_str += idx_Error1 + "개의 위생기기와 배관사이 연결할 거리가 좁습니다."; else if (idx_Error2 != 0) error_str += "\n" + idx_Error2 + "개의 파이프와 메인배관이 교차하지 않습니다."; if (error_str != "") MessageBox.Show(error_str, "오류"); } } catch { } } /// /// Y관 타입1 연결 프로세스 /// /// 메인관 /// 위생기기-메인관 직교 길이 /// 위생기기기 가장 아래쪽 커넥터 void YTeeType1Process(Pipe mainPipe, double dist_D1, XYZ Dirpt, List bottomConLst, FamilySymbol Ysymbol) { ElementId PipeSystemTypeId = mainPipe.MEPSystem.GetTypeId(); ElementId PipeTypeId = mainPipe.PipeType.Id; ElementId ViewId = mainPipe.ReferenceLevel.Id; int idx_Error1 = 0, idx_Error2 = 0; XYZ Sp = null, Ep = null; Util.GetStartEndPoint(mainPipe, ref Sp, ref Ep); bool isDir = false; //중간점에서 if (Dirpt.DistanceTo(Ep) < Dirpt.DistanceTo(Sp)) isDir = true; XYZ mainVec = null; if (isDir == true) mainVec = (Sp - Ep).Normalize(); else mainVec = (Ep - Sp).Normalize(); try { List pts = new List(); List dividePipeLst = new List(); Line mainLine = null; using (Transaction trans = new Transaction(doc)) { trans.Start("start"); for (int i = 0; i < bottomConLst.Count; i++) { pts.Clear(); Connector con = bottomConLst[i]; mainLine = (mainPipe.Location as LocationCurve).Curve as Line; XYZ mainSp = null, mainEp = null; Util.GetStartEndPoint(mainPipe, ref mainSp, ref mainEp); //Util.Pyosi(doc, mainSp, 1); //MessageBox.Show("1"); IntersectionResult interRes = mainLine.Project(con.Origin); if (interRes == null) continue; //메인과 커넥터 교차점 XYZ MainInterPt = interRes.XYZPoint; XYZ pipeEpt = Util.Polar(con.Origin, con.Origin, new XYZ(con.Origin.X, con.Origin.Y, con.Origin.Z - Unit.MMToFeet(1)), Math.Abs(MainInterPt.Z - con.Origin.Z)); //Util.Pyosi(doc, pipeEpt, 1); //D1+엘보 여유분이 위생기기-메인관 거리보다 클 경우 if (pipeEpt.DistanceTo(MainInterPt) <= Unit.MMToFeet(dist_D1 + 100)) { //위생기기와 메인배관 사이 거리가 좁습니다. idx_Error1++; continue; } pts.Add(con.Origin); pts.Add(pipeEpt); interRes = mainLine.Project(pipeEpt); //메인관과 배관 끝 꾜차점 XYZ mainDivPt = interRes.XYZPoint; XYZ vec = (mainDivPt - pipeEpt).Normalize(); //D1 길이 이동 점 XYZ D1pt = Util.Polar(pipeEpt, vec, Unit.MMToFeet(dist_D1)); pts.Add(D1pt); //Util.Pyosi(doc, D1pt, 0); //List pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, mainPipe.MEPSystem.GetTypeId(), mainPipe.PipeType.Id, mainPipe.ReferenceLevel.Id); //if (isDir == true) XYZ newVec = (D1pt - pipeEpt).Normalize(); XYZ RevNewVec = (pipeEpt - D1pt).Normalize(); XYZ rotVec1 = Util.RotateVector(newVec, Util.DTR(45)); XYZ plrpt1 = Util.Polar(D1pt, rotVec1, 200); XYZ rotVec2 = Util.RotateVector(newVec, Util.DTR(-45)); XYZ plrpt2 = Util.Polar(D1pt, rotVec2, 200); XYZ interPt1 = null, interPt2 = null; interRes = mainLine.Project(plrpt1); if (interRes == null) continue; interPt1 = interRes.XYZPoint; interRes = mainLine.Project(plrpt2); if (interRes == null) continue; interPt2 = interRes.XYZPoint; //배관 시작-끝 방향인지 XYZ isDirPoint = null; if (isDir == true) isDirPoint = mainEp; else isDirPoint = mainSp; //Util.Pyosi(doc, isDirPoint, 0); XYZ Cur45Pt = null; if (isDirPoint.DistanceTo(interPt1) < isDirPoint.DistanceTo(interPt2)) Cur45Pt = plrpt1; else Cur45Pt = plrpt2; //45도 라인과와 메인관 라인의 교차점(45피팅 절단점 찾기) XYZ inter45pt = Util.GetIntersectionPoint(mainSp, mainEp, D1pt, Cur45Pt); XYZ LineOnPt = Util.GetPointOnLine(mainLine, inter45pt); if (LineOnPt.IsAlmostEqualTo(mainEp) || LineOnPt.IsAlmostEqualTo(mainSp)) { idx_Error2++; //MessageBox.Show("메인관에 45도 파이프가 교차하지 않습니다.", "오류"); continue; } //Util.Pyosi(doc, aa, 0); pts.Add(inter45pt); List pipeLst = new List(); //위생기기-45도 파이프까지 생성 //List pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId); pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId); Pipe LastPipe = pipeLst.Last(); Connector spCon = null, epCon = null; Util.GetStartEndConnector(LastPipe, ref spCon, ref epCon); Curve LastPipeLine = (LastPipe.Location as LocationCurve).Curve; FamilyInstance yTeeFam = null; if (Ysymbol.IsActive == false) Ysymbol.Activate(); //FamilyInstance yTeeFam = uidoc.Document.Create.NewFamilyInstance(inter45pt, Ysymbol, mainVec, LastPipe, StructuralType.NonStructural); yTeeFam = uidoc.Document.Create.NewFamilyInstance(inter45pt, Ysymbol, mainVec, LastPipe, StructuralType.NonStructural); doc.Regenerate(); //y관 메인 커넥터 관경 변경 Definition df = GetDefinition(yTeeFam, "NomRad1"); Parameter param = yTeeFam.get_Parameter(df); if (param != null) param.Set(mainPipe.Diameter / 2.0); //y관 가지 커넥터 관경 변경 df = GetDefinition(yTeeFam, "NomRad2"); param = yTeeFam.get_Parameter(df); if (param != null) param.Set(LastPipe.Diameter / 2.0); doc.Regenerate(); //45도 파이프 시작-끝 방향 벡터 XYZ Vec_45Pipe = (epCon.Origin - spCon.Origin).Normalize(); List branchConLst = Util.GetBranchConnector(yTeeFam, mainPipe); //List branchConLst = Util.GetBranchConnector(yTeeFam); //삽입된 Y관Tee의 45도커넥터-Y관삽입점 방향 벡터 XYZ Vec_45TeeCon = ((yTeeFam.Location as LocationPoint).Point - branchConLst.First().Origin).Normalize(); //Y관 가지커넥터의 위치가 메인관의 오른쪽인가 bool b_BranchConRight = Util.isRightPoint(branchConLst.First().Origin, mainSp, mainEp); //45도 배관 시작점 위치가 메인관의 오른쪽인가 bool b_MainRight = Util.isRightPoint(spCon.Origin, mainSp, mainEp); //패밀리 삽입점 XYZ FamLoc = (yTeeFam.Location as LocationPoint).Point; //위의 Y관 가지커넥터, 45도 배관 시작점 둘 중 하나가 오른쪽이 아니면 Mirror로 변경 if (b_BranchConRight != b_MainRight) { //45도벡터와 메인과 벡터 교차로 z벡터구하기 XYZ crossVec1 = Vec_45TeeCon.CrossProduct(mainVec); //메인 벡터와 z벡터로 x또는y벡터 구하기 XYZ crossVec2 = mainVec.CrossProduct(crossVec1); Plane plane = Plane.CreateByNormalAndOrigin(crossVec2, FamLoc); //Y관 대칭 ElementTransformUtils.MirrorElements(doc, new List { yTeeFam.Id }, plane, false); doc.Regenerate(); } XYZ plr45pt = Util.Polar(epCon.Origin, epCon.Origin, spCon.Origin, spCon.Origin.DistanceTo(epCon.Origin) / 2.0); //Util.Pyosi(doc, plr45pt, 0); IntersectionResult interResult = mainLine.Project(plr45pt); if (interResult != null) { XYZ interpt = interResult.XYZPoint; XYZ interVec = (plr45pt - interpt).Normalize(); XYZ RevInterVec = interVec.Negate(); XYZ VecPt = Util.Polar(FamLoc, interVec, 500); XYZ ReVecPt = Util.Polar(FamLoc, RevInterVec, 500); //Y관 가지커넥터의 위치가 메인관의 오른쪽인가 bool b_Vir_BranchConRight = Util.isRightPoint(branchConLst.First().Origin, VecPt, ReVecPt); //45도 배관 시작점 위치가 메인관의 오른쪽인가 bool b_Vir_MainRight = Util.isRightPoint(spCon.Origin, VecPt, ReVecPt); if (b_Vir_BranchConRight != b_Vir_MainRight) { Plane plane = Plane.CreateByNormalAndOrigin(mainVec, FamLoc); ElementTransformUtils.MirrorElements(doc, new List { yTeeFam.Id }, plane, false); doc.Regenerate(); } } List divLst = new List(); //메인관 자르기 divLst = Util.DivideElement(doc, mainPipe, inter45pt); if (divLst.Count == 0) continue; foreach (ElementId id in divLst) { bool b_contain = dividePipeLst.Contains(id); if (b_contain == false) dividePipeLst.Add(id); } doc.Regenerate(); //자른 파이프로 변경 //mainPipe = doc.GetElement(divLst.First()) as Pipe; //자른 파이프 리스트에서 교차점이 있는 파이프 메인파이프로 변경 for (int j = 0; j < dividePipeLst.Count; j++) { if (bottomConLst.Count > i + 1) { Line divLine = (mainPipe.Location as LocationCurve).Curve as Line; Connector cons = bottomConLst[i + 1]; IntersectionResult intersection = divLine.Project(cons.Origin); if (intersection != null) { XYZ interpt = intersection.XYZPoint; List divMainConLst = Util.GetElementConnectors(mainPipe); foreach (Connector mainCon in divMainConLst) { if (interpt.IsAlmostEqualTo(mainCon.Origin)) { mainPipe = doc.GetElement(dividePipeLst[j]) as Pipe; break; } } } } } //잘린 파이프 리스트에 45도 파이프 추가 List YTeeConPipeLst = new List(); YTeeConPipeLst.AddRange(divLst); YTeeConPipeLst.Add(LastPipe.Id); //divLst.Add(LastPipe.Id); foreach (ElementId id in YTeeConPipeLst) { Pipe Connect45Pipe = doc.GetElement(id) as Pipe; //파이프 커넥터 List conLst = Util.GetElementConnectors(Connect45Pipe); foreach (Connector PipeCon in conLst) { //Y관 패밀리 삽입점과 같은 커넥터 찾기 if (PipeCon.Origin.IsAlmostEqualTo(FamLoc)) { //파이프 역방향 벡터 구하기 XYZ LineDirReVec = null; Connector pipeSpCon = null, pipeEpCon = null; Util.GetStartEndConnector(PipeCon.Owner as Pipe, ref pipeSpCon, ref pipeEpCon); if (PipeCon.Origin.IsAlmostEqualTo(pipeSpCon.Origin)) LineDirReVec = (pipeEpCon.Origin - pipeSpCon.Origin).Normalize(); else LineDirReVec = (pipeSpCon.Origin - pipeEpCon.Origin).Normalize(); //파이프 역방향으로 50mm이동한 점 XYZ plr100Pt = Util.Polar(PipeCon.Origin, LineDirReVec, Unit.MMToFeet(100)); //파이프 커넥터 위치 변경 PipeCon.Origin = plr100Pt; //파이프 커넥터와 가까운 Y관 커넥터 구하기. double minDist = double.MaxValue; Connector closeCon = null; List yFamConLst = Util.GetElementConnectors(yTeeFam); foreach (Connector yFamCon in yFamConLst) { if (yFamCon.Origin.DistanceTo(plr100Pt) < minDist) { minDist = yFamCon.Origin.DistanceTo(plr100Pt); closeCon = yFamCon; } } //파이프 커넥터 위치를 Y관 커넥터 위치로 변경 PipeCon.Origin = closeCon.Origin; //파이프 커넥퇑 Y관 커넥터 연결 PipeCon.ConnectTo(closeCon); break; } } } } trans.Commit(); } string error_str = ""; if (idx_Error1 != 0) error_str += idx_Error1 + "개의 위생기기와 메인배관 사이 거리가 좁습니다."; if (idx_Error2 != 0) error_str += "\n" + idx_Error2 + "개의 45도 파이프가 메인관에 교차하지 않습니다."; if (error_str != "") MessageBox.Show(error_str, "오류"); } catch (Exception e) { //MessageBox.Show("" + e); } } /// /// Y관 타입2 연결 프로세스 /// /// 메인관 /// 위생기기-메인관 직교 길이 /// 위생기기기 가장 아래쪽 커넥터 void YTeeType2Process(Pipe mainPipe, double dist_D1, XYZ Dirpt, List bottomConLst, FamilySymbol Ysymbol) { ElementId PipeSystemTypeId = mainPipe.MEPSystem.GetTypeId(); ElementId PipeTypeId = mainPipe.PipeType.Id; ElementId ViewId = mainPipe.ReferenceLevel.Id; int idx_Error1 = 0, idx_Error2 = 0, idx_Error3 = 0, idx_Error4 = 0; string str_Error4 = ""; double chg_MainDia = 0, chg_SubDia = 0; XYZ Sp = null, Ep = null; Util.GetStartEndPoint(mainPipe, ref Sp, ref Ep); bool isDir = false; //중간점에서 if (Dirpt.DistanceTo(Ep) < Dirpt.DistanceTo(Sp)) isDir = true; XYZ mainVec = null; if (isDir == true) mainVec = (Sp - Ep).Normalize(); else mainVec = (Ep - Sp).Normalize(); using (Transaction transGruop = new Transaction(doc)) { transGruop.Start("Start"); try { List pts = new List(); List dividePipeLst = new List(); Line mainLine = null; for (int i = 0; i < bottomConLst.Count; i++) { pts.Clear(); Connector con = bottomConLst[i]; mainLine = (mainPipe.Location as LocationCurve).Curve as Line; XYZ mainSp = null, mainEp = null; Util.GetStartEndPoint(mainPipe, ref mainSp, ref mainEp); //메인관 방향 파이프 점 구할 때 파이프 길이가 짧으면 틀어져서 구해지는 현상때문에 확장 라인 생성 XYZ ExtendMainSp = Util.Polar(mainSp, mainEp, mainSp, 200); XYZ ExtendMainEp = Util.Polar(mainEp, mainSp, mainEp, 200); Line ExtMainLine = Line.CreateBound(ExtendMainSp, ExtendMainEp); IntersectionResult interRes = ExtMainLine.Project(con.Origin); if (interRes == null) continue; //메인과 커넥터 교차점 XYZ MainInterPt = interRes.XYZPoint; //D1 길이만큼 아래로 이동한 점 XYZ pipeD1pt = Util.Polar(con.Origin, con.Origin, new XYZ(con.Origin.X, con.Origin.Y, con.Origin.Z - Unit.MMToFeet(1)), Unit.MMToFeet(dist_D1)); //메인관 교차점과 D1pt 벡터 방향 XYZ interZD1pt = new XYZ(MainInterPt.X, MainInterPt.Y, pipeD1pt.Z); XYZ connectVec = (interZD1pt - pipeD1pt).Normalize(); // D1+엘보 여유분이 위생기기-메인관 거리보다 클 경우 if (pipeD1pt.DistanceTo(interZD1pt) <= Unit.MMToFeet(dist_D1 + 100)) { //위생기기와 메인배관 사이 거리가 좁습니다. idx_Error1++; continue; } pts.Add(con.Origin); pts.Add(pipeD1pt); pts.Add(interZD1pt); //파이프 생성 //List pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId); XYZ newVec = (pipeD1pt - con.Origin).Normalize(); XYZ RevNewVec = (con.Origin - pipeD1pt).Normalize(); XYZ rotXVec1 = Util.RotateVectorX(newVec, Util.DTR(45)); XYZ plrpt1 = Util.Polar(interZD1pt, rotXVec1, 200); XYZ rotXVec2 = Util.RotateVectorX(newVec, Util.DTR(-45)); XYZ plrpt2 = Util.Polar(interZD1pt, rotXVec2, 200); ExtendMainSp = Util.Polar(mainSp, mainEp, mainSp, 200); ExtendMainEp = Util.Polar(mainEp, mainSp, mainEp, 200); XYZ interpt1 = null, interpt2 = null; //45도 라인과 메인관 라인의 교차점(45피팅 절단점 찾기) Line line1 = Line.CreateBound(ExtendMainSp, ExtendMainEp); Line line2 = Line.CreateBound(interZD1pt, plrpt1); Line line3 = Line.CreateBound(interZD1pt, plrpt2); interpt1 = Util.GetCurvesIntersectionPoint(line1, line2); if (interpt1 == null) { XYZ rotYVec1 = Util.RotateVectorY(newVec, Util.DTR(45)); plrpt1 = Util.Polar(interZD1pt, rotYVec1, 200); line2 = Line.CreateBound(interZD1pt, plrpt1); interpt1 = Util.GetCurvesIntersectionPoint(line1, line2); if (interpt1 == null) continue; } interpt2 = Util.GetCurvesIntersectionPoint(line1, line3); if (interpt2 == null) { XYZ rotYVec2 = Util.RotateVectorY(newVec, Util.DTR(-45)); plrpt2 = Util.Polar(interZD1pt, rotYVec2, 200); line3 = Line.CreateBound(interZD1pt, plrpt2); interpt2 = Util.GetCurvesIntersectionPoint(line1, line3); if (interpt2 == null) continue; } //XYZ LineOnPt = Util.GetPointOnLine(mainLine, interpt1); //Util.Pyosi(doc, interpt1, 0); //배관 시작-끝 방향인지 XYZ isDirPoint = null; if (isDir == true) isDirPoint = mainEp; else isDirPoint = mainSp; //파이프 선택 시 위치에 따른 45도 절단점 위치 설정 XYZ Cur45Pt = null; if (isDirPoint.DistanceTo(interpt1) < isDirPoint.DistanceTo(interpt2)) Cur45Pt = interpt1; else Cur45Pt = interpt2; Cur45Pt = Util.GetPointOnLine(mainLine, Cur45Pt); //45도 절단점이 메인관 시작 또는 끝점일 경우 작업 취소 if (Cur45Pt.IsAlmostEqualTo(mainEp) || Cur45Pt.IsAlmostEqualTo(mainSp)) { idx_Error2++; continue; } //(45도 점 Z - D1 점 Z) 거리가 100보다 작을 때 if (Math.Abs(pipeD1pt.Z - Cur45Pt.Z) <= Unit.MMToFeet(100)) { idx_Error3++; continue; } pts.Add(Cur45Pt); List pipeLst = new List(); Transaction trans = new Transaction(doc); //trans.Start("1"); //위생기기 -45도 파이프까지 생성 pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId); doc.Regenerate(); //trans.Commit(); Pipe LastPipe = pipeLst.Last(); Connector spCon = null, epCon = null; Util.GetStartEndConnector(LastPipe, ref spCon, ref epCon); Curve lastPipeLine = (LastPipe.Location as LocationCurve).Curve; FamilyInstance yTeeFam = null; if (Ysymbol.IsActive == false) Ysymbol.Activate(); yTeeFam = uidoc.Document.Create.NewFamilyInstance(Cur45Pt, Ysymbol, mainVec, LastPipe, StructuralType.NonStructural); chg_MainDia = Unit.FeetToMM(mainPipe.Diameter / 2.0); chg_SubDia = Unit.FeetToMM(LastPipe.Diameter / 2.0); if (mainPipe.Diameter == Unit.MMToFeet(35) || mainPipe.Diameter == Unit.MMToFeet(40) || mainPipe.Diameter == Unit.MMToFeet(50) || mainPipe.Diameter == Unit.MMToFeet(75) || mainPipe.Diameter == Unit.MMToFeet(80) || mainPipe.Diameter == Unit.MMToFeet(100) || mainPipe.Diameter == Unit.MMToFeet(125)) { //Y관 메인 커넥터 관경 변경 Definition df = GetDefinition(yTeeFam, "NomRad1"); Parameter param = yTeeFam.get_Parameter(df); if (param != null) param.Set(mainPipe.Diameter / 2.0); } else { //Y관 삭제 Util.Delete(doc, yTeeFam); idx_Error4++; string tmp = Unit.FeetToMM(mainPipe.Diameter) + "x" + Unit.FeetToMM(LastPipe.Diameter) + " Y관은 지원하지 않습니다."; bool b_error = str_Error4.Contains(tmp); if(b_error==false) str_Error4 += tmp; //trans.Commit(); continue; } if (LastPipe.Diameter == Unit.MMToFeet(35) || LastPipe.Diameter == Unit.MMToFeet(40) || LastPipe.Diameter == Unit.MMToFeet(50) || LastPipe.Diameter == Unit.MMToFeet(75) || LastPipe.Diameter == Unit.MMToFeet(80) || LastPipe.Diameter == Unit.MMToFeet(100) || LastPipe.Diameter == Unit.MMToFeet(125)) { //Y관 가지 커넥터 관경 변경 Definition df = GetDefinition(yTeeFam, "NomRad2"); Parameter param = yTeeFam.get_Parameter(df); if (param != null) param.Set(LastPipe.Diameter / 2.0); } else { //Y관 삭제 Util.Delete(doc, yTeeFam); idx_Error4++; string tmp = Unit.FeetToMM(mainPipe.Diameter) + "x" + Unit.FeetToMM(LastPipe.Diameter) + " Y관은 지원하지 않습니다."; bool b_error = str_Error4.Contains(tmp); if (b_error == false) str_Error4 += tmp; //trans.Commit(); continue; } doc.Regenerate(); //trans.Start("3"); //45도 파이프 시작-끝 방향 벡터 XYZ Vec_45Pipe = (epCon.Origin - spCon.Origin).Normalize(); List branchConLst = Util.GetBranchConnector(yTeeFam, mainPipe); //패밀리 삽입점 XYZ FamLoc = (yTeeFam.Location as LocationPoint).Point; interRes = mainLine.Project(spCon.Origin); //메인관 교차점과 D1pt방향 벡터와 반대반향 벡터 //XYZ connectVec = (interZD1pt - pipeD1pt).Normalize(); XYZ ReConnectVec = connectVec.Negate(); XYZ VecPt = Util.Polar(FamLoc, connectVec, Unit.MMToFeet(500)); XYZ ReVecPt = Util.Polar(FamLoc, ReConnectVec, Unit.MMToFeet(500)); //Y관 가지 커넥터 위치가 메인관의 오른쪽인가 bool b_Vir_BranchConRIght = Util.isRightPoint(branchConLst.First().Origin, VecPt, ReVecPt); //45도 배관 시작점 위치가 메인관의 오른쪽인가 bool b_Vir_MainRight = Util.isRightPoint(spCon.Origin, VecPt, ReVecPt); //다를 경우 Mirror로 돌리기 if (b_Vir_BranchConRIght != b_Vir_MainRight) { Plane plane = Plane.CreateByNormalAndOrigin(mainVec, FamLoc); ElementTransformUtils.MirrorElements(doc, new List { yTeeFam.Id }, plane, false); doc.Regenerate(); } //45도 파이프 시작-끝 방향 벡터와 삽입된 Y관Tee의 45도커넥터-Y관삽입점 방향 벡터가 //같을 때까지 반복하여90도로 돌리기 while (true) { ElementTransformUtils.RotateElement(doc, yTeeFam.Id, mainLine, Util.DTR(90)); doc.Regenerate(); //삽입된 Y관Tee의 45도커넥터-Y관삽입점 방향 벡터 XYZ Vec_45TeeCon = ((yTeeFam.Location as LocationPoint).Point - branchConLst.First().Origin).Normalize(); if (Vec_45Pipe.IsAlmostEqualTo(Vec_45TeeCon)) break; }//while end List divLst = new List(); //메인관 자르기 divLst = Util.DivideElement(doc, mainPipe, epCon.Origin); if (divLst.Count == 0) continue; foreach (ElementId id in divLst) { bool b_contain = dividePipeLst.Contains(id); if (b_contain == false) dividePipeLst.Add(id); } doc.Regenerate(); //trans.Commit(); //자른 파이프로 변경 //자른 파이프 리스트에서 교차점이 있는 파이프 메인파이프로 변경 for (int j = 0; j < dividePipeLst.Count; j++) { if (bottomConLst.Count > i + 1) { Line divLine = (mainPipe.Location as LocationCurve).Curve as Line; Connector cons = bottomConLst[i + 1]; IntersectionResult intersection = divLine.Project(cons.Origin); if (intersection != null) { XYZ interpt = intersection.XYZPoint; List divMainConLst = Util.GetElementConnectors(mainPipe); foreach (Connector mainCon in divMainConLst) { if (interpt.IsAlmostEqualTo(mainCon.Origin)) { mainPipe = doc.GetElement(dividePipeLst[j]) as Pipe; break; } } } } }//for end //잘린 파이프 리스트에 45도 파이프 추가 List YteeConPipeLst = new List(); YteeConPipeLst.AddRange(divLst); YteeConPipeLst.Add(LastPipe.Id); //trans.Start("4"); foreach (ElementId id in YteeConPipeLst) { Pipe Connect45Pipe = doc.GetElement(id) as Pipe; //파이프 커넥터 List conLst = Util.GetElementConnectors(Connect45Pipe); foreach (Connector PipeCon in conLst) { //Y관 패밀리 삽입점과 같은 커넥터 찾기 if (PipeCon.Origin.IsAlmostEqualTo(FamLoc)) { //파이프 역방향 벡터 구하기 XYZ LineDirReVec = null; Connector pipeSpCon = null, pipeEpCon = null; Util.GetStartEndConnector(PipeCon.Owner as Pipe, ref pipeSpCon, ref pipeEpCon); if (PipeCon.Origin.IsAlmostEqualTo(pipeSpCon.Origin)) LineDirReVec = (pipeEpCon.Origin - pipeSpCon.Origin).Normalize(); else LineDirReVec = (pipeSpCon.Origin - pipeEpCon.Origin).Normalize(); //파이프 역방향으로 100mm 이동한 점 XYZ plr100Pt = Util.Polar(PipeCon.Origin, LineDirReVec, Unit.MMToFeet(100)); //파이프 커넥터 위치 변경 PipeCon.Origin = plr100Pt; //파이프 커넥터와 가까운 Y관 커넥터 구하기. double mainDist = double.MaxValue; Connector closeCon = null; List yFamConLst = Util.GetElementConnectors(yTeeFam); foreach (Connector yFamCon in yFamConLst) { if (yFamCon.Origin.DistanceTo(plr100Pt) < mainDist) { mainDist = yFamCon.Origin.DistanceTo(plr100Pt); closeCon = yFamCon; } } //파이프 커넥터 위치를 Y관 커넥터 위치로 변경 PipeCon.Origin = closeCon.Origin; //파이프 커넥퇑 Y관 커넥터 연결 PipeCon.ConnectTo(closeCon); break; } } }//foreach end //trans.Commit(); }//for end string error_str = ""; if (idx_Error1 != 0) error_str += idx_Error1 + "개의 위생기기와 배관사이 연결할 높이가 좁습니다."; //MessageBox.Show(idx_Error + "개의 45도 파이프가 메인관에 교차하지 않습니다.", "오류"); if (idx_Error2 != 0) error_str += "\n" + idx_Error2 + "개의 45도 파이프와 메인배관이 교차하지 않습니다."; if (idx_Error3 != 0) error_str += "\n" + idx_Error3 + "개의 45도 파이프가 작도할 높이가 좁습니다."; if (idx_Error4 != 0) error_str += "\n" + str_Error4; if (error_str != "") MessageBox.Show(error_str, "오류"); } catch (Exception e) { //MessageBox.Show("" + e); } transGruop.Commit(); } } //패밀리 커넥터 중 가장 아래쪽 커넥터 구하기. Connector GetBottonConnector(FamilyInstance family) { List FamConLst = Util.GetElementConnectors(family); double minOffset = double.MaxValue; Connector bottomCon = null; foreach (Connector con in FamConLst) { if (con.Origin.Z < minOffset) { bottomCon = con; minOffset = con.Origin.Z; } } //Util.Pyosi(doc, bottomCon.Origin, 1); return bottomCon; } /// /// 매개 변수 정의를 반환합니다. /// 주어진 요소와 매개 변수 이름. /// static Definition GetDefinition(Element elem, string parameter_name) { IList ps = elem.GetParameters(parameter_name); int n = ps.Count; Debug.Assert(1 >= n, "expected maximum one shared parameters" + "named " + parameter_name); Definition d = (0 == n) ? null : ps[0].Definition; return d; } public class PipeSelectionFilter : ISelectionFilter { public bool AllowElement(Element element) { if (element.Category == null) return false; if (element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeCurves) { return true; } return false; } public bool AllowReference(Reference refer, XYZ point) { return false; } } public class HygienicSelectionFilter : ISelectionFilter { public bool AllowElement(Element element) { if (element.Category == null) return false; if (element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PlumbingFixtures) { return true; } return false; } public bool AllowReference(Reference refer, XYZ point) { return false; } } } }