using System.Collections.Generic; using System.Diagnostics; using System.Windows; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Electrical; using Autodesk.Revit.DB.Mechanical; using Autodesk.Revit.DB.Plumbing; namespace Sai.Toolkit.Revit.Assist; /// /// MEP扩展类 /// /// 管线连接件CoordinateSystem的BasisX为连接件的(-BasisZ叉乘BasisY) /// 管线连接件CoordinateSystem的BasisY为连接件的底部向下 /// 管线连接件CoordinateSystem的BasisZ为连接件的法向朝外 public static class MEPAssist { /// /// 从连接件延伸长度 /// /// /// public static double GetExtensionLength(this MEPCurve mepCurve) { foreach (Connector conn in mepCurve.GetConnectors()) { return conn.GetExtensionLength(); } return -1; } /// /// 两条异面直线的公垂线(两直线距离长度最短)与异面直线的交点 /// /// /// /// public static Dictionary GetNearestPoints(this MEPCurve mepCurve, MEPCurve mepCurve1) { Dictionary dictionary = []; var line1 = mepCurve.GetCurve() as Line; var line2 = mepCurve1.GetCurve() as Line; var a = line1.Direction.DotProduct(line2.Direction); var b = line1.Direction.DotProduct(line1.Direction); var c = line2.Direction.DotProduct(line2.Direction); var d = (line2.GetEndPoint(0) - line1.GetEndPoint(0)).DotProduct(line1.Direction); var e = (line2.GetEndPoint(0) - line1.GetEndPoint(0)).DotProduct(line2.Direction); double t1; double t2; if (a == 0) //表明原始两条直线互相垂直 { t1 = d / b; t2 = -e / c; } else if (Math.Abs((a * a) - (b * c)) < 0.0001f) //表明共线或平行,注意理想情况下应该是a * radian - b * c == 0,才认为共线或平行,但在计算机世界里,有精度这个东西存在,所以我们近视的认为数值小于某一个值则认为等于0 { //说明共线或平行 return dictionary; } else { t1 = ((a * e) - (c * d)) / ((a * a) - (b * c)); t2 = (b / a * t1) - (d / a); } var p1 = line1.GetEndPoint(0) + (t1 * line1.Direction); var p2 = line2.GetEndPoint(0) + (t2 * line2.Direction); dictionary.Add(mepCurve, p1); dictionary.Add(mepCurve1, p2); return dictionary; } /// /// 复制并修改定位线,并可用来连接该管线,朝上方向的一致性 /// /// 用来复制的管线 /// 定位线 /// public static MEPCurve CopyAndSetLocationCurve(this MEPCurve mepCurve, Line line) { var doc = mepCurve.Document; var elemId = ElementTransformUtils.CopyElement(doc, mepCurve.Id, XYZ.Zero).FirstOrDefault(); var newMepCurve = doc.GetElement(elemId) as MEPCurve; newMepCurve.SetLocationCurve(line); doc.Regenerate(); //立管的断面可能错误,需要旋转截面 if (line.Direction.CrossProduct(XYZ.BasisZ).IsZeroLength()) { var conns = ConnectorAssist.GetNearestConnectors(mepCurve, newMepCurve); var hand = conns[0].CoordinateSystem.BasisX; //取反向 var newHand = conns[1].CoordinateSystem.BasisX; //点积 var cos = hand.DotProduct(newHand); //夹角 var radian = Math.Acos(cos); ////旋转轴向 //var d = hand.CrossProduct(newHand); //当两个BasisX相反时,则为可正确连接的,不需要旋转 if (hand.IsAlmostEqualTo(-newHand)) { return newMepCurve; } //连接件的BasisZ就是轴向 var d = conns[1].CoordinateSystem.BasisZ; //以立管或倾斜管作为参考轴 var ax = Line.CreateUnbound(conns[1].Origin, d); //保证夹角为180度,故旋转角用Π-夹角 ElementTransformUtils.RotateElement(doc, elemId, ax, Math.PI - radian); //double radian = default; //Line axis = null; //foreach (Connector connector in mepCurve.ConnectorManager.Connectors) //{ // foreach (Connector connector1 in newMepCurve.ConnectorManager.Connectors) // { // //侧面向量 // var dotProduct = connector1.CoordinateSystem.BasisX.DotProduct(connector.CoordinateSystem.BasisX); // radian = Math.Acos(dotProduct); // //为0的时候即两个侧面向量重合或相反,表明要么正好合适,要么需要旋转180度, // var direction = // Math.Sin(radian) < 0.0001 ? XYZ.BasisZ : connector1.CoordinateSystem.BasisX.CrossProduct(connector.CoordinateSystem.BasisX); // if (!direction.IsZeroLength()) // { // axis = Line.CreateUnbound(connector1.Origin, direction); // } // //radian = Math.PI - radian; // } //} //ElementTransformUtils.RotateElement(doc, elemId, axis, radian); } return newMepCurve; } /// /// 用点打断管线 /// /// 原管线,执行后,保留为打断点到原管线终点 /// 打断点 /// 新的管线为原来管线定位线方向,在打断点到原管线起点部分 /// 新的管线 public static ElementId BreakByPoint(this MEPCurve mepCurve, XYZ point) { if (mepCurve is not InsulationLiningBase) { try { var document = mepCurve.Document; return mepCurve switch { Duct duct => MechanicalUtils.BreakCurve(document, duct.Id, point), Pipe pipe => PlumbingUtils.BreakCurve(document, pipe.Id, point), _ => mepCurve.BreakCurve(point) }; } catch (Exception e) { Debug.WriteLine(e.Message); return ElementId.InvalidElementId; } } throw new InvalidOperationException($"{nameof(mepCurve)}不是管线或不可打断"); } /// /// 打断直线管线 /// /// /// /// 新创建的管线距离起点近,返回Id=-1时,不能打断管线 /// private static ElementId BreakCurve(this MEPCurve electricMepCurve, XYZ point) { var document = electricMepCurve.Document; var application = document.Application; var conns = electricMepCurve.ConnectorManager.Connectors.OfType().Where(c => c.ConnectorType == ConnectorType.End); //原管线连接的接头 var originConn1 = conns.ElementAt(0); var originConn2 = conns.ElementAt(1); //已连接的连接件 var connectedConn1 = originConn1.GetConnectedConnector(); var connectedConn2 = originConn2.GetConnectedConnector(); Debug.WriteLine($"起点连接件{connectedConn1}"); Debug.WriteLine($"终点连接件{connectedConn2}"); connectedConn1?.DisconnectFrom(originConn1); connectedConn2?.DisconnectFrom(originConn2); document.Regenerate(); //投影点 if ( electricMepCurve.GetCurve() is not Line line || electricMepCurve is FlexDuct || electricMepCurve is FlexPipe || electricMepCurve is PipeInsulation || electricMepCurve is DuctInsulation ) { //throw new InvalidOperationException("不可打断的类型"); return ElementId.InvalidElementId; } var projectPoint = line.Project(point).XYZPoint; if ((electricMepCurve.GetCurve() as Line).IsInsideEx(point, application.ShortCurveTolerance)) { var startLine = Line.CreateBound(line.GetEndPoint(0), projectPoint); var endLine = Line.CreateBound(projectPoint, line.GetEndPoint(1)); var electricMepCurveCopyId = ElementTransformUtils.CopyElement(document, electricMepCurve.Id, XYZ.Zero).FirstOrDefault(); var electricMepCurveCopy = document.GetElement(electricMepCurveCopyId) as MEPCurve; //起点线 electricMepCurveCopy.SetLocationCurve(startLine); electricMepCurve.SetLocationCurve(endLine); Debug.WriteLine($"设置定位线"); document.Regenerate(); Debug.WriteLine($"原有接头{connectedConn1?.Origin}"); Debug.WriteLine($"原有接头{connectedConn2?.Origin}"); foreach (Connector conn1 in electricMepCurveCopy!.ConnectorManager.Connectors) { Debug.WriteLine($"复制管连接件{conn1.Origin}"); if (connectedConn1 != null) { if (conn1.Origin.DistanceTo(connectedConn1.Origin) < application.ShortCurveTolerance) { Debug.WriteLine("复制管连接连接件1"); conn1.ConnectTo(connectedConn1); break; } } if (connectedConn2 != null) { if (conn1.Origin.DistanceTo(connectedConn2.Origin) < application.ShortCurveTolerance) { Debug.WriteLine("复制管连接连接件2"); conn1.ConnectTo(connectedConn2); break; } } } foreach (Connector conn1 in electricMepCurve!.ConnectorManager.Connectors) { if (connectedConn1 != null) { if (conn1.Origin.DistanceTo(connectedConn1.Origin) < application.ShortCurveTolerance) { Debug.WriteLine("原有管连接连接件1"); conn1.ConnectTo(connectedConn1); break; } } if (connectedConn2 != null) { if (conn1.Origin.DistanceTo(connectedConn2.Origin) < application.ShortCurveTolerance) { Debug.WriteLine("原有管连接连接件2"); conn1.ConnectTo(connectedConn2); break; } } } return electricMepCurveCopyId; } return ElementId.InvalidElementId; } public static FamilySymbol GetFittingFromConduit( this ConduitType condType, RoutingPreferenceRuleGroupType groupType, double pipeDiameter, Document doc ) { FamilySymbol familySymbol = null; var routingPreferenceManager = condType.RoutingPreferenceManager; for (var num = 0; num != routingPreferenceManager.GetNumberOfRules(groupType); num++) { var rule = routingPreferenceManager.GetRule(groupType, num); var primarySizeCriterion = rule.GetCriterion(0) as PrimarySizeCriterion; if ( Math.Round(primarySizeCriterion.MinimumSize * 12.0, 2) <= Math.Round(pipeDiameter, 2) && Math.Round(pipeDiameter, 2) <= Math.Round(primarySizeCriterion.MaximumSize * 12.0, 2) ) { var meppartId = rule.MEPPartId; familySymbol = doc.GetElement(meppartId) as FamilySymbol; if (!familySymbol.Name.Contains("Tap")) { break; } } } return familySymbol; } public static FamilySymbol GetFittingFromRouting(this PipeType pipeType, RoutingPreferenceRuleGroupType groupType, double pipeDiameter, Document doc) { FamilySymbol familySymbol = null; var routingPreferenceManager = pipeType.RoutingPreferenceManager; for (var num = 0; num != routingPreferenceManager.GetNumberOfRules(groupType); num++) { var rule = routingPreferenceManager.GetRule(groupType, num); var primarySizeCriterion = rule.GetCriterion(0) as PrimarySizeCriterion; if ( Math.Round(primarySizeCriterion.MinimumSize * 12.0, 2) <= Math.Round(pipeDiameter, 2) && Math.Round(pipeDiameter, 2) <= Math.Round(primarySizeCriterion.MaximumSize * 12.0, 2) ) { var meppartId = rule.MEPPartId; familySymbol = doc.GetElement(meppartId) as FamilySymbol; if (!familySymbol.Name.Contains("Tap")) { break; } } } return familySymbol; } /// /// 添加管道尺寸 /// /// /// 公制 /// /// public static void AddPipeSize(this Pipe pipe, double nominal, double inner, double outer) { var pt = pipe.PipeType; var rpm = pt.RoutingPreferenceManager; var rpr = rpm.GetRule(RoutingPreferenceRuleGroupType.Segments, 0); //获取管段设置数量 //var eid = rpr.MEPPartId; //int num = rpm.GetNumberOfRules(RoutingPreferenceRuleGroupType.Segments); //PipeSegment ps = doc.GetElement(eid) as PipeSegment; //存在多个管段,获取第一个 var ps = pipe.PipeSegment; MEPSize mepSize = new(nominal / 304.8, inner / 304.8, outer / 304.8, true, true); ps.AddSize(mepSize); //RoutingPreferenceRule rule = new RoutingPreferenceRule(eid, ""); if (rpr.GetCriterion(0) is PrimarySizeCriterion psc && psc.MaximumSize < nominal / 304.8) { psc.MaximumSize = nominal / 304.8; } //rpr.AddCriterion(new PrimarySizeCriterion(6 / 304.8, pipesize)); //rpm.AddRule(RoutingPreferenceRuleGroupType.Segments, rpr); //pipe.get_Parameter(BuiltInParameter.RBS_CURVE_VERT_OFFSET_PARAM).Set(2);//顶部 //pipe.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).Set(pipesize); } /// /// 主次分支连接 /// /// 主管 /// 分支 public static void ConnectTo(this MEPCurve mainMepCurve, MEPCurve branchMepCurve) { var document = mainMepCurve.Document; var unboundBranchCurve = branchMepCurve.GetCurve(); unboundBranchCurve.MakeUnbound(); var unboundMainCurve = mainMepCurve.GetCurve(); unboundMainCurve.MakeUnbound(); var intersection = unboundBranchCurve.IntersectionPoint(unboundMainCurve) ?? throw new InvalidOperationException("主次分支管线不存在交点"); var line = mainMepCurve.GetCurve() as Line; //根据交点在管线内的位置,判断是形成弯头还是三通 if (line.IsInsideEx(intersection, 0.5)) //三通 { var newMainMEPCurveId = mainMepCurve.BreakByPoint(intersection); var newMainMEPCurve = document.GetElement(newMainMEPCurveId) as MEPCurve; var mainConnectors = ConnectorAssist.GetNearestConnectors(mainMepCurve, newMainMEPCurve); var branchConnector = branchMepCurve.GetConnectors(true).GetNearestConnector(intersection); branchConnector.ConnectByTee(mainConnectors[0], mainConnectors[1]); } else //弯头 { var conn = mainMepCurve.GetConnectors(true).GetNearestConnector(intersection); var conn2 = branchMepCurve.GetConnectors(true).GetNearestConnector(intersection); conn.ConnectByFitting(conn2); } } /// /// 获取所有连接元素 /// /// /// 连接的所有元素ID public static void GetAllConnectedElements(this Element mepCurve, List elemIdsConnected) { var allConnectors = mepCurve.GetConnectors(); foreach (Connector connector in allConnectors) { if (!connector.IsConnected) { continue; } if (!elemIdsConnected.Contains(connector.Owner.Id)) { elemIdsConnected.Add(connector.Owner.Id); } //连接的元素 //得到与当前连接件连接的连接件 foreach (Connector currentConn in connector.AllRefs) { //连接的连接件 if (currentConn.ConnectorType == ConnectorType.End && currentConn.Owner.Id != connector.Owner.Id) { if (elemIdsConnected.Contains(currentConn.Owner.Id)) { continue; } elemIdsConnected.Add(currentConn.Owner.Id); GetAllConnectedElements(currentConn.Owner, elemIdsConnected); //var conns = currentConn.Owner.GetConnectors(); //foreach (Connector item in conns) //{ // if (startConnector.Origin.IsAlmostEqualTo(item.Origin)) // { // continue; // } // GetAllRefsElements(item, elemIdsConnected); //} } } } } /// /// 管线与墙的相交(射线法) /// /// public static List GetPipeIntersector(this Pipe pipe) { var doc = pipe.Document; var v = doc.ActiveView as View3D; List li = []; //线段长度 var pline = pipe.GetCurve() as Line; var p1 = pline.GetEndPoint(0); var p2 = pline.GetEndPoint(1); //线段方向 var dir = (p2 - p1).Normalize(); var length = pline.Length; ElementClassFilter filter = new(typeof(T)); ReferenceIntersector rinter = new(filter, FindReferenceTarget.Element, v); var rwc = rinter.Find(p1, dir); foreach (var r in rwc) { if (r.Proximity < length) { var elem = doc.GetElement(r.GetReference()); if (elem is T t) { li.Add(t); } } } return li; //MessageBox.Show("墙的数量为:" + i, "提示", MessageBoxButton.OK, MessageBoxImage.Information); } }