using Autodesk.Revit.DB; using Autodesk.Revit.DB.Electrical; using Autodesk.Revit.DB.Structure; using FWindSoft.Revit.Utils; using FWindSoft.SystemExtensions; using FWindSoft.Tools; using System; using System.Collections.Generic; using System.Linq; namespace FWindSoft.Revit.Mep { /// /// 线管管件创建 /// public class ConduitFittingCreator { /* * 预留扩展:使用客户端传入的自定族; * 可能处理与管件类型相关操作,故各个管线分开处理 */ /// /// FamilyInstance自动和给定的Connectors进行相连接 /// /// /// /// private static void AutoMatchJoin(FamilyInstance instance, List connectors, JoinContext context) { //此数据源不进行ToList处理,为了后边连接状态发生改变时,遍历对象能自动跟着改变 var fiConnectors = instance.GetAllConnectors().Where(c => c.IsPhysical()); //次等匹配,精确匹配连接没有找到完成的,再进行次等连接 List secondConnectors = new List(); #region 精确匹配连接,并构件次等匹配数据级 foreach (Connector connector in connectors) { if (!connector.IsPhysical() || connector.IsConnected) { continue; } var refConnector = ConnectorUtils.GetConnectorByDirection(fiConnectors.ToList(), -connector.CoordinateSystem.BasisZ); if (refConnector == null) { secondConnectors.Add(connector); continue; } if (!refConnector.IsConnected) { refConnector.Radius = connector.Radius; try { if (connector.Owner is Conduit conduit) { //细微处理,有的不进行此步骤,管道长度会自己调整。有的情况则需要自己调整管道,不可预知,所以暂时统一处理 conduit.ReplaceLocation(connector.Origin, refConnector.Origin); } refConnector.ConnectTo(connector); } catch (Exception e) {//为了调试时,查看具体异常。保留e变量的引用 //return false; } } } #endregion #region 次等匹配连接 foreach (Connector connector in secondConnectors) { //在没有连接的Connector里面去选 var minAngleConnector = ConnectorUtils.GetConnectorByMinAngle(fiConnectors.Where(c => !c.IsConnected).ToList(), -connector.CoordinateSystem.BasisZ, Math.Cos(Math.PI / 4)); if (minAngleConnector != null) { ArrangeConnectorJoin(minAngleConnector, connector, context); } } #endregion } /// /// fi以外的Connector连接自身Connector /// /// /// /// private static bool ConnectorJoin(FamilyInstance fi, Connector otherConnector, JoinContext context) { var refConnector = fi.GetConnectorByDirection(-otherConnector.CoordinateSystem.BasisZ); if (refConnector != null) { refConnector.Radius = otherConnector.Radius; try { if (otherConnector.Owner is Conduit conduit) { //细微处理,有的不进行此步骤,管道长度会自己调整。有的情况则需要自己调整管道,不可预知,所以暂时统一处理 conduit.ReplaceLocation(otherConnector.Origin, refConnector.Origin); } refConnector.ConnectTo(otherConnector); } catch (Exception e) {//为了调试时,查看具体异常。保留e变量的引用 return false; } return true; } else { var minAngleConnector = fi.GetConnectorByMinAngle(-otherConnector.CoordinateSystem.BasisZ); if (minAngleConnector != null) { return ArrangeConnectorJoin(minAngleConnector, otherConnector, context); } } return false; } /// /// 整理连接(处理Connector朝向未平行的情况) /// /// 设备的连接点 /// 管道的连接点 /// /// private static bool ArrangeConnectorJoin(Connector fiConnector, Connector conduitConnector, JoinContext context) { try { if (fiConnector == null || conduitConnector == null) { return false; } var length = JoinConduitUtils.AttachLength; var useMepCurve = conduitConnector.Owner as MEPCurve; var start = new XYZ(fiConnector.Origin.X, fiConnector.Origin.Y, fiConnector.Origin.Z); var end = start + fiConnector.CoordinateSystem.BasisZ * length; var newMepCurve = useMepCurve.Copy(start, end); useMepCurve.ReplaceLocation(conduitConnector.Origin, end); fiConnector.ConnectTo(newMepCurve.GetConnectorByOrigin(start)); var newEndConnector = newMepCurve.GetConnectorByOrigin(end); //这个最好要换成自己封装的两个Connector连接的方法 //newEndConnector.ConnectTo(conduitConnector); NewElbowFitting(newEndConnector, conduitConnector, context); } catch (Exception e) { return false; } return true; } #region 封装处理 指定连接构件 连接的两种思路 /* * 1、根据扩展数据中存储的类型id,调用创建构件的不同重载方法 * 2、根据扩展数据中存储的类型id,动态更改线管连接的连接规则,当然此规则设置成为优先及最高,包含范围 * 全部。这样程序在直接使用NewTeeFitting等api。执行完成之后,再将规则删除 */ #endregion #region 活接头连接封装 /// /// 活接头连接 /// /// /// /// /// public static FamilyInstance NewUnionFitting(Connector connector1, Connector connector2, JoinContext context) { if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected)) { //已经连接部分 return null; } FamilyInstance fi = null; if (connector1.CoordinateSystem.BasisZ.IsSameDirection(-connector2.CoordinateSystem.BasisZ)) { fi = connector1.Owner.Document.Create.NewUnionFitting(connector1, connector2); } return fi; } #endregion #region 变径连接封装 /// /// 变径连接 /// /// /// /// /// public static FamilyInstance NewTransitionFitting(Connector connector1, Connector connector2, JoinContext context) { if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected)) { //已经连接部分 return null; } FamilyInstance fi = null; if (connector1.CoordinateSystem.BasisZ.IsSameDirection(-connector2.CoordinateSystem.BasisZ)) { fi = connector1.Owner.Document.Create.NewTransitionFitting(connector1, connector2); } return fi; } #endregion /// /// 弯通连接 /// /// /// /// /// public static FamilyInstance NewElbowFitting(Connector connector1, Connector connector2, JoinContext context) { if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected)) { //已经连接部分 return null; } FamilyInstance fi = null; var doc = connector1.Owner.Document; var dotValue = connector1.CoordinateSystem.BasisZ.DotProduct(connector2.CoordinateSystem.BasisZ); if (dotValue.IsZero()) { //垂直 //var refDirection = connector1.CoordinateSystem.BasisZ.IsParallel(XYZ.BasisZ) ? connector2.CoordinateSystem.BasisZ : connector1.CoordinateSystem.BasisZ; FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-弯头(五孔)"); var line1 = Line.CreateUnbound(connector1.Origin, connector1.CoordinateSystem.BasisZ); var line2 = Line.CreateUnbound(connector2.Origin, connector2.CoordinateSystem.BasisZ); var location = line1.GetSpatialIntersection(line2); var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural); FittingWrapper fitting = new FittingWrapper(box); fitting.Rotate(location, -connector1.CoordinateSystem.BasisZ, -connector2.CoordinateSystem.BasisZ); JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, new List() { -connector1.CoordinateSystem.BasisZ, -connector2.CoordinateSystem.BasisZ }); ConnectorJoin(box, connector1, context); ConnectorJoin(box, connector2, context); fi = box; } else if (dotValue.Less(0)) { //钝角连接 //使用可以调整角度的连接件,需要调整角度. #region 利用系统创建弯头可以自行修改弯头角度的优势,创建钝角弯头 var useType = connector1.Owner.GetElementType(); if (useType == null) { useType = connector2.Owner.GetElementType(); } if (useType != null) { var oldFitting = FittingUtils.GetFitting(useType, FittingType.Elbows); try { FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管弯头 - 平端口 - PVC"); FittingUtils.AddFitting(useType, FittingType.Elbows, fs?.Id); fi = doc.Create.NewElbowFitting(connector1, connector2); } finally { FittingUtils.AddFitting(useType, FittingType.Elbows, oldFitting); } } #endregion } else { //锐角连接. XYZ baseDirection = -connector1.CoordinateSystem.BasisZ; XYZ refDirection = -connector2.CoordinateSystem.BasisZ; var axis = baseDirection.CrossProduct(refDirection); var result = baseDirection.RotateVector(axis, Math.PI / 2); var line1 = Line.CreateUnbound(connector1.Origin, connector1.CoordinateSystem.BasisZ); var line2 = Line.CreateUnbound(connector2.Origin, connector2.CoordinateSystem.BasisZ); var location = line1.GetSpatialIntersection(line2); FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-弯头(五孔)"); //var fiDirection= baseDirection.IsParallel(XYZ.BasisZ) ? axis : baseDirection; var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural); FittingWrapper fitting = new FittingWrapper(box); fitting.Rotate(location, baseDirection, result); JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, new List() { baseDirection, result }); ConnectorJoin(box, connector1, context); var newConnector = box.GetConnectorByDirection(result); ArrangeConnectorJoin(newConnector, connector2, context); fi = box; } return fi; } #region 三通连接创建 /// /// 三通连接 /// /// connector1需要与connector3垂直 /// /// /// /// public static FamilyInstance NewTeeFitting(Connector connector1, Connector connector2, Connector connector3, JoinContext context) { if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected || connector3.IsConnected)) { //已经连接部分 return null; } List newConnectors = ConnectorPatternUtils.ArrangeTeeConnectors(connector1, connector2, connector3); if(newConnectors==null|| newConnectors.Count!=3) { return null; } var doc = connector1.Owner.Document; var line1 = Line.CreateUnbound(newConnectors[0].Origin, newConnectors[0].CoordinateSystem.BasisZ); var line3 = Line.CreateUnbound(newConnectors[2].Origin, newConnectors[2].CoordinateSystem.BasisZ); var location = line1.GetSpatialIntersection(line3); //放置三通,手动连接节点 FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-三通(五孔)"); var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural); #region 初步定位实现 /* * 默认是1,3是垂直的,判定第第二个是否满足右手坐标系,不然调转3的方向传入旋转 */ XYZ direction1 = -newConnectors[0].CoordinateSystem.BasisZ; XYZ direction2 = -newConnectors[1].CoordinateSystem.BasisZ; XYZ direction3 = -newConnectors[2].CoordinateSystem.BasisZ; FittingWrapper fitting = new FittingWrapper(box); var baseJudge = direction1.CrossProduct(direction3); var dotValue = baseJudge.DotProduct(direction2);//小于0时,影响右手坐标系形成,进行调整;1和2头可能平行,所以调整1 XYZ matchFirst = dotValue.MoreEqual(0) ? direction1 : -direction1;//由于默认连接件定点朝上,使用时,反向使用 fitting.Rotate(location, matchFirst, direction3); #endregion JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, new List() { direction1, direction3, direction2 }); AutoMatchJoin(box, newConnectors, context); return box; } #endregion /// /// 四通连接 /// /// connector1需要与Connector3垂直 /// /// /// /// /// public static FamilyInstance NewCrossFitting(Connector connector1, Connector connector2, Connector connector3, Connector connector4, JoinContext context) { if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected || connector3.IsConnected || connector4.IsConnected)) { //已经连接部分 return null; } Document doc = connector1.Owner.Document; List inputConnectors = new List() { connector1, connector2, connector3, connector4 }; Connector baseConnector = connector1; Connector useConnector = null; for (int i = 1; i < inputConnectors.Count; i++) { if (!baseConnector.CoordinateSystem.BasisZ.IsParallel(inputConnectors[i].CoordinateSystem.BasisZ)) { useConnector = inputConnectors[i]; break; } } var line1 = Line.CreateUnbound(baseConnector.Origin, baseConnector.CoordinateSystem.BasisZ); var line3 = Line.CreateUnbound(useConnector.Origin, useConnector.CoordinateSystem.BasisZ); var location = line1.GetSpatialIntersection(line3); FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-四通"); var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural); #region 根据样式,调整旋转 FittingWrapper fitting = new FittingWrapper(box); var firstDirection = -line1.Direction; var secondDirection = -line3.Direction; var otherConnectors = inputConnectors.Where(c => c != baseConnector && c != useConnector).ToList(); var crossDirection = firstDirection.CrossProduct(secondDirection); var comValue = -Math.Sqrt(2) / 2;//比较值,夹角cos值小于该值得,变换旋转方向 if (otherConnectors.Any(c => (-c.CoordinateSystem.BasisZ).DotProduct(crossDirection).LessEqual(comValue))) { firstDirection = -firstDirection; } fitting.Rotate(location, firstDirection, secondDirection); #endregion var useMatchDirection = inputConnectors.Select(c => -c.CoordinateSystem.BasisZ).ToList(); JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, useMatchDirection); AutoMatchJoin(box, inputConnectors, context); return box; } /// /// 创建五通连接 /// /// /// /// /// /// /// /// public static FamilyInstance NewFiveLinksFitting(Connector connector1, Connector connector2, Connector connector3, Connector connector4, Connector connector5, JoinContext context) { if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected || connector3.IsConnected || connector4.IsConnected || connector5.IsConnected)) { //已经连接部分 return null; } Document doc = connector1.Owner.Document; List inputConnectors = new List() { connector1, connector2, connector3, connector4, connector5 }; Connector baseConnector = connector1; Connector useConnector = null; for (int i = 1; i < inputConnectors.Count; i++) { if (!baseConnector.CoordinateSystem.BasisZ.IsParallel(inputConnectors[i].CoordinateSystem.BasisZ)) { useConnector = inputConnectors[i]; break; } } var line1 = Line.CreateUnbound(baseConnector.Origin, baseConnector.CoordinateSystem.BasisZ); var line3 = Line.CreateUnbound(useConnector.Origin, useConnector.CoordinateSystem.BasisZ); var location = line1.GetSpatialIntersection(line3); FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-四通"); var box = doc.Create.NewFamilyInstance(location, fs, StructuralType.NonStructural); #region 根据样式,调整旋转 FittingWrapper fitting = new FittingWrapper(box); var firstDirection = -line1.Direction; var secondDirection = -line3.Direction; var otherConnectors = inputConnectors.Where(c => c != baseConnector && c != useConnector).ToList(); var crossDirection = firstDirection.CrossProduct(secondDirection); var comValue = -Math.Sqrt(2) / 2;//比较值,夹角cos值小于该值得,变换旋转方向 if (otherConnectors.Any(c => (-c.CoordinateSystem.BasisZ).DotProduct(crossDirection).LessEqual(comValue))) { firstDirection = -firstDirection; } fitting.Rotate(location, firstDirection, secondDirection); #endregion var useMatchDirection = inputConnectors.Select(c => -c.CoordinateSystem.BasisZ).ToList(); JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, useMatchDirection); AutoMatchJoin(box, inputConnectors, context); return box; } } }