using Autodesk.Revit.UI; using System; using System.Collections.Generic; using System.Linq; using System.Text; using Autodesk.Revit.DB; using Autodesk.Revit.UI.Selection; using System.Threading.Tasks; using KDCS.Utils; using System.Collections; using System.Windows.Forms; using Autodesk.Revit.DB.Plumbing; using Autodesk.Revit.DB.Mechanical; using System.Xml.Linq; using Autodesk.Revit.UI.Events; using System.IO; using KMBIM.Revit.Tools; using KMBIM.Revit.Tools.Cmd.ColorSort; using System.Resources; using KMBIM.Revit.Tools.Cmd.PipeMatchInterval; using KMBIM.Revit.Tools.Cmd.DuctOffsetConnect; using Autodesk.Revit.DB.Structure; using Autodesk.Revit.DB.Events; namespace KMBIM { /// /// 1. 두 덕트를 선택한다. /// 2. 페밀리를 삽입한다. /// 3. 파라미터를 일치 시킨다. /// 4. 1에서 선택한 지점에서 제일 가까운 커넥터 두 개 중 Z값 높은 것에 페밀리가 맞도록 페밀리 이동 /// 5. 가까운 덕트의 커넥터와 페밀리 커넥터를 Fitting /// [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] public class DuctOffsetConnect : IExternalCommand { UIApplication uiapp; UIDocument uidoc; static public Autodesk.Revit.DB.Document doc = null; Autodesk.Revit.ApplicationServices.Application app = null; public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { try { if (!WorkMain.GetInstance().IsValid) return Autodesk.Revit.UI.Result.Succeeded; while (true) { // Verify active document if (null == commandData.Application.ActiveUIDocument.Document) { message = "현재 활성화된 뷰가 없습니다."; return Result.Failed; } uiapp = commandData.Application; uidoc = uiapp.ActiveUIDocument; app = uiapp.Application; doc = uidoc.Document; Reference r1 = uidoc.Selection.PickObject(ObjectType.Element, "첫번째 덕트 지정"); Reference r2 = uidoc.Selection.PickObject(ObjectType.Element, "두번째 덕트 지정"); XYZ selPt1 = r1.GlobalPoint; XYZ selPt2 = r2.GlobalPoint; if (r1 == null || r2 == null) return Result.Failed; Duct duct1 = doc.GetElement(r1.ElementId) as Duct; Duct duct2 = doc.GetElement(r2.ElementId) as Duct; //덕트 간격 띄우기 값으로 정렬 List duList = new List { duct1, duct2 }; duList.Sort((a, b) => a.get_Parameter(BuiltInParameter.RBS_OFFSET_PARAM).AsDouble(). CompareTo(b.get_Parameter(BuiltInParameter.RBS_OFFSET_PARAM).AsDouble())); Duct dt1 = duList[1];//상대적으로 높이 있는 배관 Duct dt2 = duList[0];//낮은 배관 if (dt1 == null || dt2 == null) { MessageBox.Show("덕트를 선택하십시오.", "오류"); return Result.Failed; } double duct1Height = dt1.get_Parameter(BuiltInParameter.RBS_CURVE_HEIGHT_PARAM).AsDouble();//높이 double duct1Width = dt2.get_Parameter(BuiltInParameter.RBS_CURVE_WIDTH_PARAM).AsDouble();//폭 //선택한 지점과 가장 가까운 커넥터 찾기 ConnectorSet cs1 = dt1.ConnectorManager.Connectors; ConnectorSet cs2 = dt2.ConnectorManager.Connectors; Connector insertCon1 = GetConnectorClosestTo(cs1, selPt1); Connector insertCon2 = GetConnectorClosestTo(cs2, selPt2); List insertConList = new List { insertCon1, insertCon2 }; insertConList.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z)); XYZ pt1 = new XYZ(); XYZ pt2 = new XYZ(); //무조건 Z값 큰게 pt1 pt1 = insertConList[1].Origin;//Z값 큰 포인트 pt2 = insertConList[0].Origin;//z값 작은 포인트 XYZ pt1pt2Vec = (pt2 - pt1).Normalize(); AngleSelect ags = new AngleSelect(); ags.ShowDialog(); double angleValue = Util.DTR(ags.inputValue);//유저에게 입력받은 각도 값 if (ags.isConfirmed == false) return Result.Cancelled; using (Transaction tr = new Transaction(doc)) { tr.Start("Transaction Start"); FamilySymbol fs = LoadFamily("T_Rectangular_UPDOWN");//페밀리 로드 if (fs.IsActive == false) fs.Activate(); FamilyInstance fi = null; Connector con1 = null, con2 = null, con3 = null, con4 = null; Util.GetStartEndConnector(dt1, ref con1, ref con2); Util.GetStartEndConnector(dt2, ref con3, ref con4); //Util.Pyosi(doc, con1.Origin, 0); //Util.Pyosi(doc, con2.Origin, 0); List conLst = new List(); conLst.Add(con1); conLst.Add(con2); conLst.Add(con3); conLst.Add(con4); Connector MinCon1 = null, MinCon2 = null; double minDist = double.MaxValue; foreach (Connector DirCon in conLst) { foreach (Connector con in conLst) { if (Util.GetNextElementConnector(con) != null) continue; if (con.Origin.IsAlmostEqualTo(DirCon.Origin)) continue; //길이 최솟값 구하기 if (con.Origin.DistanceTo(DirCon.Origin) < minDist) { minDist = con.Origin.DistanceTo(DirCon.Origin); MinCon1 = con; MinCon2 = DirCon; } } } //삽입점을 구하기 위해 Z값 높은 커넥터 구하기 Connector HgtCon = null; if (MinCon1.Origin.Z > MinCon2.Origin.Z) HgtCon = MinCon1; else HgtCon = MinCon2; //Util.Pyosi(doc, HgtCon.Origin, 0); List fiConList = new List(); XYZ HgtVec = null; if (Util.IsEqual(con1.Origin.Z, con2.Origin.Z, Unit.MMToFeet(5)))//삽입해야 할 덕트들이 가로로 그려진 경우 { Connector otherHgtCon = Util.GetOtherConnector(HgtCon); HgtVec = (HgtCon.Origin - otherHgtCon.Origin).Normalize();//Z값 높은 덕트의 벡터 구하기 //페밀리 삽입 fi = doc.Create.NewFamilyInstance(HgtCon.Origin, fs, HgtVec, HgtCon.Owner, Autodesk.Revit.DB.Structure.StructuralType.NonStructural); doc.Regenerate(); double pt1Mpt2 = pt1.Z - pt2.Z;//패밀리 높이값 지정을 위해 높이 구하기 //Paramter 조정 SyncDuctParameters(fi, duct1Height, duct1Width, angleValue, pt1Mpt2); doc.Regenerate(); fiConList = Util.GetElementConnectors(fi);//페밀리 커넥터 //Z값 기준으로 정리. Z값 작은 값부터 나열(오름차순) fiConList.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z)); XYZ famPt1 = fiConList[1].Origin;//z값 큰 포인트 XYZ famPt2 = fiConList[0].Origin;//Z값 작은 포인트 double dist = pt2.DistanceTo(famPt2); XYZ vec = (pt2 - famPt2).Normalize() * dist; //덕트 이동 ElementTransformUtils.MoveElement(doc, fi.Id, vec); insertConList[1].Origin = fiConList[1].Origin; fiConList[1].ConnectTo(insertConList[1]); insertConList[0].Origin = fiConList[0].Origin; fiConList[0].ConnectTo(insertConList[0]); } else//삽입해야 할 덕트들이 수직으로 그려진 경우 { MessageBox.Show("S 덕트 기능은 수직 덕트에는 적용되지 않습니다.", "오류"); return Result.Cancelled; //fi = doc.Create.NewFamilyInstance(HgtCon.Origin, fs, StructuralType.NonStructural); //doc.Regenerate(); // //fiConList = Util.GetElementConnectors(fi);//페밀리 커넥터 // ////Z값 기준으로 정리. Z값 작은 값부터 나열(오름차순) //fiConList.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z)); // //XYZ famPt1 = fiConList[1].Origin;//z값 큰 포인트 //XYZ famPt2 = fiConList[0].Origin;//Z값 작은 포인트 //XYZ famMidPt = Util.Midpoint(famPt1, famPt2); //double HeightForVertical = Math.Abs(pt1.Z - pt2.Z); // ////Paramter 조정 //SyncDuctParameters(fi, duct1Height, duct1Width, angleValue, HeightForVertical); //doc.Regenerate(); // ////pt2에서 Z축 방향으로 연장한 직선을 만들어서 pt1을 투영시킨다. ////투영된 점(newPt)과 pt1 벡터를 구해서 이를 기준으로 pt1->famPt 벡터를 만들어서 ////두 벡터의 각도를 구하고, 이 각도만큼 페밀리를 회전시켜 방향을 맞춘다. //XYZ pt2ZEx = new XYZ(pt2.X, pt2.Y, pt2.Z + 100); //Line zExtension = Line.CreateBound(pt2, pt2ZEx); //IntersectionResult ir = zExtension.Project(pt1); //XYZ newPt = ir.XYZPoint; // //XYZ pt1ToNewPtVec = (newPt - pt1).Normalize(); //XYZ pt1TofamPt1Vec = (famPt2 - pt1).Normalize(); // //double an = Util.RTD(pt1TofamPt1Vec.AngleTo(pt1ToNewPtVec)); // //RotateFamilyZAxis2(fi, famPt1, -an); //doc.Regenerate(); // ////페밀리 위치가 바뀌었기 때문에 패밀리 커넥터 좌표 세롭게 조사 //List newConList = Util.GetElementConnectors(fi); //newConList.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z)); //famPt1 = newConList[1].Origin; //famPt2 = newConList[0].Origin; // //Line famAxis = Line.CreateBound(famPt1, famPt2); ////ElementTransformUtils.RotateElement(doc, fi.Id, famAxis, Util.DTR(180)); //doc.Regenerate(); // ////페밀리 위치가 바뀌었기 때문에 패밀리 커넥터 좌표 세롭게 조사 //List newConList2 = Util.GetElementConnectors(fi); //newConList2.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z)); //famPt1 = newConList[1].Origin; //famPt2 = newConList[0].Origin; // //double mVal = famPt1.Z - pt1.Z; //XYZ newLoc = new XYZ(famPt1.X, famPt1.Y, famPt1.Z - mVal); //MoveFamilyInstance(doc, fi, newLoc); //doc.Regenerate(); // //RotateFamilyZAxis(fi, famPt1, famPt2, 180); // //insertConList[1].Origin = fiConList[1].Origin; //fiConList[1].ConnectTo(insertConList[1]); //insertConList[0].Origin = fiConList[0].Origin; //fiConList[0].ConnectTo(insertConList[0]); //Connector m_dt1Con1 = null, m_dt1Con2 = null, m_dt2Con1 = null, m_dt2Con2 = null; //Util.GetStartEndConnector(dt1, ref m_dt1Con1, ref m_dt1Con2); //Util.GetStartEndConnector(dt2, ref m_dt2Con1, ref m_dt2Con2); // //List m_dtConLst1 = Util.GetElementConnectors(dt1); //List m_dtConLst2 = Util.GetElementConnectors(dt2); // //Connector closeCon1 = null, closeCon2 = null; //double min2C2 = double.MaxValue; //foreach(Connector dtcon1 in m_dtConLst1) //{ // foreach (Connector dtcon2 in m_dtConLst2) // { // if (dtcon1.Origin.DistanceTo(dtcon2.Origin) < min2C2) // { // min2C2 = dtcon1.Origin.DistanceTo(dtcon2.Origin); // closeCon1 = dtcon1; // closeCon2 = dtcon2; // } // } //} // //Util.Pyosi(doc, closeCon1.Origin); //Util.Pyosi(doc, closeCon2.Origin); ////두 덕트의 가까운 커넥터의 X축이 같은지 //if(Math.Abs(closeCon1.Origin.X - closeCon2.Origin.X) < 0.001) //{ // fi = doc.Create.NewFamilyInstance(closeCon1.Origin, fs, StructuralType.NonStructural); //}//두 덕트의 가까운 커넥터의 Y축이 같은지 //else if (Math.Abs(closeCon1.Origin.Y - closeCon2.Origin.Y) < 0.001) //{ // //} } doc.Regenerate(); tr.Commit(); } } } catch (Exception e) { //MessageBox.Show(e.Message); } return Result.Succeeded; } public Connector GetConnectorClosestTo(ConnectorSet connectors, XYZ p) { Connector targetConnector = null; double minDist = double.MaxValue; foreach (Connector c in connectors) { double d = c.Origin.DistanceTo(p); if (d < minDist) { targetConnector = c; minDist = d; } } return targetConnector; } public FamilySymbol LoadFamily(string familyName) { FamilySymbol fs = null; //태그 패밀리 불러오기 FilteredElementCollector symbolcollector = new FilteredElementCollector(doc); ICollection symbolcollection = symbolcollector.OfCategory(BuiltInCategory.OST_DuctFitting).ToElements(); foreach (Element element in symbolcollection) { FamilySymbol symbol = element as FamilySymbol; if (symbol != null) { if (symbol.FamilyName == familyName) { fs = symbol; break; } } } //패밀리가 도면에 로드되어 있지 않을 경우 로드 , 경로 버전별로 나눔 if (fs == null) { string versionNumber = doc.Application.VersionNumber.ToString(); string tmp = Util.GetKMBIMLibraryFolder("Libraries\\DuctOffsetConnect"); string fileName = null; if (familyName == "T_Rectangular_UPDOWN") { fileName = tmp + "\\" + versionNumber + "\\T_Rectangular_UPDOWN.rfa"; } Family fam = null; //doc.LoadFamilySymbol(fileName, familyName, out fs); doc.LoadFamily(fileName, out fam); if (fam != null) { ISet fam_ids = fam.GetFamilySymbolIds(); List fam_sym_names = new List(); if (fam_ids.Count() == 0) return null; fs = (FamilySymbol)doc.GetElement(fam_ids.ElementAt(0)); } } return fs; } // 패밀리 위치 이동(패밀리 인스턴스는 Transform 이용해야 해서 만듦) public static void MoveFamilyInstance(Document doc, FamilyInstance family, XYZ MovePt) { try { Transform transform = family.GetTransform(); XYZ Translation = MovePt - transform.Origin; //패밀리 이동 ElementTransformUtils.MoveElement(doc, family.Id, Translation); //redraw / 트랜잭션 실행문 안에서 할 것. doc.Regenerate(); } catch { } } public void SyncDuctParameters(FamilyInstance fi, double duct1Height, double duct1Width, double angleValue, double pt1Mpt2) { Parameter heightParam = fi.LookupParameter("Height"); Parameter famDuctHeightParam = fi.LookupParameter("Duct Height"); Parameter famDuctWidthParam = fi.LookupParameter("Duct Width"); Parameter famAngleParam = fi.LookupParameter("Angle"); //Paramter 조정 heightParam.Set(pt1Mpt2); famDuctHeightParam.Set(duct1Height); famDuctWidthParam.Set(duct1Width); famAngleParam.Set(angleValue); } public void AdjustingAndFittingFamilyWithDuct(XYZ pt1, XYZ famPt1, FamilyInstance fi, Connector insertCon1, Connector insertCon0, Connector familyCon1, Connector familyCon0) { double famPt1MPt1 = famPt1.Z - pt1.Z; XYZ newFamLoc = new XYZ(pt1.X, pt1.Y, pt1.Z - famPt1MPt1); MoveFamilyInstance(doc, fi, newFamLoc); doc.Regenerate(); insertCon1.Origin = familyCon1.Origin; familyCon1.ConnectTo(insertCon1);//pt1과 패밀리 z값 큰 포인트랑 fitting insertCon0.Origin = familyCon0.Origin; familyCon0.ConnectTo(insertCon0); } /// /// 패밀리를 X축 기준으로 회전시킨다. 메서드 안에서 라디안으로 바꿔줌. /// /// /// /// public void RotateFamilyXAxis(FamilyInstance fi, XYZ famPt1, XYZ famPt2, double angle) { XYZ famMidPt = Util.Midpoint(famPt1, famPt2); XYZ ptForZAxis = new XYZ(famMidPt.X + 100, famMidPt.Y, famMidPt.Z); Line zAxis = Line.CreateBound(famMidPt, ptForZAxis); ElementTransformUtils.RotateElement(doc, fi.Id, zAxis, Util.DTR(angle)); } /// /// 패밀리를 Y축 기준으로 회전시킨다. 메서드 안에서 라디안으로 바꿔줌. /// /// /// /// public void RotateFamilyYAxis(FamilyInstance fi, XYZ famPt1, XYZ famPt2, double angle) { XYZ famMidPt = Util.Midpoint(famPt1, famPt2); XYZ ptForZAxis = new XYZ(famMidPt.X, famMidPt.Y + 100, famMidPt.Z); Line zAxis = Line.CreateBound(famMidPt, ptForZAxis); ElementTransformUtils.RotateElement(doc, fi.Id, zAxis, Util.DTR(angle)); } /// /// 패밀리를 Z축 기준으로 회전시킨다. 메서드 안에서 라디안으로 바꿔줌. /// /// /// /// public void RotateFamilyZAxis(FamilyInstance fi, XYZ famPt1, XYZ famPt2, double angle) { XYZ famMidPt = Util.Midpoint(famPt1, famPt2); XYZ ptForZAxis = new XYZ(famMidPt.X, famMidPt.Y, famMidPt.Z + 100); Line zAxis = Line.CreateBound(famMidPt, ptForZAxis); ElementTransformUtils.RotateElement(doc, fi.Id, zAxis, Util.DTR(angle)); } public void RotateFamilyZAxis2(FamilyInstance fi, XYZ famPt1, double angle) { XYZ ptForZAxis = new XYZ(famPt1.X, famPt1.Y, famPt1.Z + 100); Line zAxis = Line.CreateBound(famPt1, ptForZAxis); ElementTransformUtils.RotateElement(doc, fi.Id, zAxis, Util.DTR(angle)); } public XYZ GetDuctVector(Duct dt) { List cList = Util.GetElementConnectors(dt); XYZ pt1 = cList[0].Origin; XYZ pt2 = cList[1].Origin; XYZ vec = (pt2 - pt1).Normalize(); return vec; } /// /// 덕트 경사도 구하기 /// /// 덕트 /// 덕트 경사도 퍼센테이지 public double GetDuctSlopePercentage(Duct dt) { double slope = dt.get_Parameter(BuiltInParameter.RBS_DUCT_SLOPE).AsDouble(); double slopePer = slope * 100;//경사도 return slopePer; } public void ModifyingDuctForVerticalDuct(FamilyInstance fi, XYZ famMidPt, XYZ famPt1, XYZ famPt2) { RotateFamilyZAxis(fi, famPt1, famPt2, 180); XYZ extentionToY = new XYZ(famMidPt.X, famMidPt.Y + 100, famMidPt.Z); Line axis = Line.CreateBound(famMidPt, extentionToY); ElementTransformUtils.RotateElement(doc, fi.Id, axis, Util.DTR(90)); doc.Regenerate(); } public void ModifyingDuctForVerticalDuctWithoutFilp(FamilyInstance fi, XYZ famMidPt, XYZ famPt1, XYZ famPt2, List fiConList, XYZ pt1) { //Y축 기준으로 90도 회전 XYZ extentionToY = new XYZ(famMidPt.X, famMidPt.Y + 100, famMidPt.Z); Line axis = Line.CreateBound(famMidPt, extentionToY); ElementTransformUtils.RotateElement(doc, fi.Id, axis, Util.DTR(90)); doc.Regenerate(); //아래로 내리기 double famPt1MPt1Z = fiConList[0].Origin.Z - pt1.Z; XYZ newFamLoc = new XYZ(pt1.X, pt1.Y, pt1.Z - famPt1MPt1Z); MoveFamilyInstance(doc, fi, newFamLoc); doc.Regenerate(); //좌로 이동 double famPt2MPt1X = Math.Abs(fiConList[1].Origin.X - pt1.X); XYZ newFamLoc2 = new XYZ(pt1.X - famPt2MPt1X, pt1.Y, pt1.Z); MoveFamilyInstance(doc, fi, newFamLoc2); doc.Regenerate(); } public double GetMinValue(List doubleList) { double minLength = doubleList[0]; for (int i = 0; i < doubleList.Count(); i++) { //최솟값 구하기 if (minLength > doubleList[i]) { minLength = doubleList[i]; } } return minLength; } } }