ConduitFittingCreator.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. using Autodesk.Revit.DB;
  2. using Autodesk.Revit.DB.Electrical;
  3. using Autodesk.Revit.DB.Structure;
  4. using FWindSoft.Revit.Utils;
  5. using FWindSoft.SystemExtensions;
  6. using FWindSoft.Tools;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. namespace FWindSoft.Revit.Mep
  11. {
  12. /// <summary>
  13. /// 线管管件创建
  14. /// </summary>
  15. public class ConduitFittingCreator
  16. {
  17. /*
  18. * 预留扩展:使用客户端传入的自定族;
  19. * 可能处理与管件类型相关操作,故各个管线分开处理
  20. */
  21. /// <summary>
  22. /// FamilyInstance自动和给定的Connectors进行相连接
  23. /// </summary>
  24. /// <param name="instance"></param>
  25. /// <param name="connectors"></param>
  26. /// <param name="context"></param>
  27. private static void AutoMatchJoin(FamilyInstance instance, List<Connector> connectors, JoinContext context)
  28. {
  29. //此数据源不进行ToList处理,为了后边连接状态发生改变时,遍历对象能自动跟着改变
  30. var fiConnectors = instance.GetAllConnectors().Where(c => c.IsPhysical());
  31. //次等匹配,精确匹配连接没有找到完成的,再进行次等连接
  32. List<Connector> secondConnectors = new List<Connector>();
  33. #region 精确匹配连接,并构件次等匹配数据级
  34. foreach (Connector connector in connectors)
  35. {
  36. if (!connector.IsPhysical() || connector.IsConnected)
  37. {
  38. continue;
  39. }
  40. var refConnector = ConnectorUtils.GetConnectorByDirection(fiConnectors.ToList(), -connector.CoordinateSystem.BasisZ);
  41. if (refConnector == null)
  42. {
  43. secondConnectors.Add(connector);
  44. continue;
  45. }
  46. if (!refConnector.IsConnected)
  47. {
  48. refConnector.Radius = connector.Radius;
  49. try
  50. {
  51. if (connector.Owner is Conduit conduit)
  52. {
  53. //细微处理,有的不进行此步骤,管道长度会自己调整。有的情况则需要自己调整管道,不可预知,所以暂时统一处理
  54. conduit.ReplaceLocation(connector.Origin, refConnector.Origin);
  55. }
  56. refConnector.ConnectTo(connector);
  57. }
  58. catch (Exception e)
  59. {//为了调试时,查看具体异常。保留e变量的引用
  60. //return false;
  61. }
  62. }
  63. }
  64. #endregion
  65. #region 次等匹配连接
  66. foreach (Connector connector in secondConnectors)
  67. {
  68. //在没有连接的Connector里面去选
  69. var minAngleConnector = ConnectorUtils.GetConnectorByMinAngle(fiConnectors.Where(c => !c.IsConnected).ToList(), -connector.CoordinateSystem.BasisZ, Math.Cos(Math.PI / 4));
  70. if (minAngleConnector != null)
  71. {
  72. ArrangeConnectorJoin(minAngleConnector, connector, context);
  73. }
  74. }
  75. #endregion
  76. }
  77. /// <summary>
  78. /// fi以外的Connector连接自身Connector
  79. /// </summary>
  80. /// <param name="fi"></param>
  81. /// <param name="otherConnector"></param>
  82. /// <returns></returns>
  83. private static bool ConnectorJoin(FamilyInstance fi, Connector otherConnector, JoinContext context)
  84. {
  85. var refConnector = fi.GetConnectorByDirection(-otherConnector.CoordinateSystem.BasisZ);
  86. if (refConnector != null)
  87. {
  88. refConnector.Radius = otherConnector.Radius;
  89. try
  90. {
  91. if (otherConnector.Owner is Conduit conduit)
  92. {
  93. //细微处理,有的不进行此步骤,管道长度会自己调整。有的情况则需要自己调整管道,不可预知,所以暂时统一处理
  94. conduit.ReplaceLocation(otherConnector.Origin, refConnector.Origin);
  95. }
  96. refConnector.ConnectTo(otherConnector);
  97. }
  98. catch (Exception e)
  99. {//为了调试时,查看具体异常。保留e变量的引用
  100. return false;
  101. }
  102. return true;
  103. }
  104. else
  105. {
  106. var minAngleConnector = fi.GetConnectorByMinAngle(-otherConnector.CoordinateSystem.BasisZ);
  107. if (minAngleConnector != null)
  108. {
  109. return ArrangeConnectorJoin(minAngleConnector, otherConnector, context);
  110. }
  111. }
  112. return false;
  113. }
  114. /// <summary>
  115. /// 整理连接(处理Connector朝向未平行的情况)
  116. /// </summary>
  117. /// <param name="fiConnector">设备的连接点</param>
  118. /// <param name="conduitConnector">管道的连接点</param>
  119. /// <param name="context"></param>
  120. /// <returns></returns>
  121. private static bool ArrangeConnectorJoin(Connector fiConnector, Connector conduitConnector, JoinContext context)
  122. {
  123. try
  124. {
  125. if (fiConnector == null || conduitConnector == null)
  126. {
  127. return false;
  128. }
  129. var length = JoinConduitUtils.AttachLength;
  130. var useMepCurve = conduitConnector.Owner as MEPCurve;
  131. var start = new XYZ(fiConnector.Origin.X, fiConnector.Origin.Y, fiConnector.Origin.Z);
  132. var end = start + fiConnector.CoordinateSystem.BasisZ * length;
  133. var newMepCurve = useMepCurve.Copy(start, end);
  134. useMepCurve.ReplaceLocation(conduitConnector.Origin, end);
  135. fiConnector.ConnectTo(newMepCurve.GetConnectorByOrigin(start));
  136. var newEndConnector = newMepCurve.GetConnectorByOrigin(end);
  137. //这个最好要换成自己封装的两个Connector连接的方法
  138. //newEndConnector.ConnectTo(conduitConnector);
  139. NewElbowFitting(newEndConnector, conduitConnector, context);
  140. }
  141. catch (Exception e)
  142. {
  143. return false;
  144. }
  145. return true;
  146. }
  147. #region 封装处理 指定连接构件 连接的两种思路
  148. /*
  149. * 1、根据扩展数据中存储的类型id,调用创建构件的不同重载方法
  150. * 2、根据扩展数据中存储的类型id,动态更改线管连接的连接规则,当然此规则设置成为优先及最高,包含范围
  151. * 全部。这样程序在直接使用NewTeeFitting等api。执行完成之后,再将规则删除
  152. */
  153. #endregion
  154. #region 活接头连接封装
  155. /// <summary>
  156. /// 活接头连接
  157. /// </summary>
  158. /// <param name="connector1"></param>
  159. /// <param name="connector2"></param>
  160. /// <param name="context"></param>
  161. /// <returns></returns>
  162. public static FamilyInstance NewUnionFitting(Connector connector1, Connector connector2, JoinContext context)
  163. {
  164. if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected))
  165. {
  166. //已经连接部分
  167. return null;
  168. }
  169. FamilyInstance fi = null;
  170. if (connector1.CoordinateSystem.BasisZ.IsSameDirection(-connector2.CoordinateSystem.BasisZ))
  171. {
  172. fi = connector1.Owner.Document.Create.NewUnionFitting(connector1, connector2);
  173. }
  174. return fi;
  175. }
  176. #endregion
  177. #region 变径连接封装
  178. /// <summary>
  179. /// 变径连接
  180. /// </summary>
  181. /// <param name="connector1"></param>
  182. /// <param name="connector2"></param>
  183. /// <param name="context"></param>
  184. /// <returns></returns>
  185. public static FamilyInstance NewTransitionFitting(Connector connector1, Connector connector2, JoinContext context)
  186. {
  187. if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected))
  188. {
  189. //已经连接部分
  190. return null;
  191. }
  192. FamilyInstance fi = null;
  193. if (connector1.CoordinateSystem.BasisZ.IsSameDirection(-connector2.CoordinateSystem.BasisZ))
  194. {
  195. fi = connector1.Owner.Document.Create.NewTransitionFitting(connector1, connector2);
  196. }
  197. return fi;
  198. }
  199. #endregion
  200. /// <summary>
  201. /// 弯通连接
  202. /// </summary>
  203. /// <param name="connector1"></param>
  204. /// <param name="connector2"></param>
  205. /// <param name="context"></param>
  206. /// <returns></returns>
  207. public static FamilyInstance NewElbowFitting(Connector connector1, Connector connector2, JoinContext context)
  208. {
  209. if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected))
  210. {
  211. //已经连接部分
  212. return null;
  213. }
  214. FamilyInstance fi = null;
  215. var doc = connector1.Owner.Document;
  216. var dotValue = connector1.CoordinateSystem.BasisZ.DotProduct(connector2.CoordinateSystem.BasisZ);
  217. if (dotValue.IsZero())
  218. {
  219. //垂直
  220. //var refDirection = connector1.CoordinateSystem.BasisZ.IsParallel(XYZ.BasisZ) ? connector2.CoordinateSystem.BasisZ : connector1.CoordinateSystem.BasisZ;
  221. FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-弯头(五孔)");
  222. var line1 = Line.CreateUnbound(connector1.Origin, connector1.CoordinateSystem.BasisZ);
  223. var line2 = Line.CreateUnbound(connector2.Origin, connector2.CoordinateSystem.BasisZ);
  224. var location = line1.GetSpatialIntersection(line2);
  225. var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural);
  226. FittingWrapper fitting = new FittingWrapper(box);
  227. fitting.Rotate(location, -connector1.CoordinateSystem.BasisZ, -connector2.CoordinateSystem.BasisZ);
  228. JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, new List<XYZ>() { -connector1.CoordinateSystem.BasisZ, -connector2.CoordinateSystem.BasisZ });
  229. ConnectorJoin(box, connector1, context);
  230. ConnectorJoin(box, connector2, context);
  231. fi = box;
  232. }
  233. else if (dotValue.Less(0))
  234. {
  235. //钝角连接
  236. //使用可以调整角度的连接件,需要调整角度.
  237. #region 利用系统创建弯头可以自行修改弯头角度的优势,创建钝角弯头
  238. var useType = connector1.Owner.GetElementType<ConduitType>();
  239. if (useType == null)
  240. {
  241. useType = connector2.Owner.GetElementType<ConduitType>();
  242. }
  243. if (useType != null)
  244. {
  245. var oldFitting = FittingUtils.GetFitting(useType, FittingType.Elbows);
  246. try
  247. {
  248. FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管弯头 - 平端口 - PVC");
  249. FittingUtils.AddFitting(useType, FittingType.Elbows, fs?.Id);
  250. fi = doc.Create.NewElbowFitting(connector1, connector2);
  251. }
  252. finally
  253. {
  254. FittingUtils.AddFitting(useType, FittingType.Elbows, oldFitting);
  255. }
  256. }
  257. #endregion
  258. }
  259. else
  260. {
  261. //锐角连接.
  262. XYZ baseDirection = -connector1.CoordinateSystem.BasisZ;
  263. XYZ refDirection = -connector2.CoordinateSystem.BasisZ;
  264. var axis = baseDirection.CrossProduct(refDirection);
  265. var result = baseDirection.RotateVector(axis, Math.PI / 2);
  266. var line1 = Line.CreateUnbound(connector1.Origin, connector1.CoordinateSystem.BasisZ);
  267. var line2 = Line.CreateUnbound(connector2.Origin, connector2.CoordinateSystem.BasisZ);
  268. var location = line1.GetSpatialIntersection(line2);
  269. FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-弯头(五孔)");
  270. //var fiDirection= baseDirection.IsParallel(XYZ.BasisZ) ? axis : baseDirection;
  271. var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural);
  272. FittingWrapper fitting = new FittingWrapper(box);
  273. fitting.Rotate(location, baseDirection, result);
  274. JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, new List<XYZ>() { baseDirection, result });
  275. ConnectorJoin(box, connector1, context);
  276. var newConnector = box.GetConnectorByDirection(result);
  277. ArrangeConnectorJoin(newConnector, connector2, context);
  278. fi = box;
  279. }
  280. return fi;
  281. }
  282. #region 三通连接创建
  283. /// <summary>
  284. /// 三通连接
  285. /// </summary>
  286. /// <param name="connector1">connector1需要与connector3垂直</param>
  287. /// <param name="connector2"></param>
  288. /// <param name="connector3"></param>
  289. /// <param name="context"></param>
  290. /// <returns></returns>
  291. public static FamilyInstance NewTeeFitting(Connector connector1, Connector connector2, Connector connector3, JoinContext context)
  292. {
  293. if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected || connector3.IsConnected))
  294. {
  295. //已经连接部分
  296. return null;
  297. }
  298. List<Connector> newConnectors = ConnectorPatternUtils.ArrangeTeeConnectors(connector1, connector2, connector3);
  299. if(newConnectors==null|| newConnectors.Count!=3)
  300. {
  301. return null;
  302. }
  303. var doc = connector1.Owner.Document;
  304. var line1 = Line.CreateUnbound(newConnectors[0].Origin, newConnectors[0].CoordinateSystem.BasisZ);
  305. var line3 = Line.CreateUnbound(newConnectors[2].Origin, newConnectors[2].CoordinateSystem.BasisZ);
  306. var location = line1.GetSpatialIntersection(line3);
  307. //放置三通,手动连接节点
  308. FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-三通(五孔)");
  309. var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural);
  310. #region 初步定位实现
  311. /*
  312. * 默认是1,3是垂直的,判定第第二个是否满足右手坐标系,不然调转3的方向传入旋转
  313. */
  314. XYZ direction1 = -newConnectors[0].CoordinateSystem.BasisZ;
  315. XYZ direction2 = -newConnectors[1].CoordinateSystem.BasisZ;
  316. XYZ direction3 = -newConnectors[2].CoordinateSystem.BasisZ;
  317. FittingWrapper fitting = new FittingWrapper(box);
  318. var baseJudge = direction1.CrossProduct(direction3);
  319. var dotValue = baseJudge.DotProduct(direction2);//小于0时,影响右手坐标系形成,进行调整;1和2头可能平行,所以调整1
  320. XYZ matchFirst = dotValue.MoreEqual(0) ? direction1 : -direction1;//由于默认连接件定点朝上,使用时,反向使用
  321. fitting.Rotate(location, matchFirst, direction3);
  322. #endregion
  323. JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, new List<XYZ>() { direction1, direction3, direction2 });
  324. AutoMatchJoin(box, newConnectors, context);
  325. return box;
  326. }
  327. #endregion
  328. /// <summary>
  329. /// 四通连接
  330. /// </summary>
  331. /// <param name="connector1">connector1需要与Connector3垂直</param>
  332. /// <param name="connector2"></param>
  333. /// <param name="connector3"></param>
  334. /// <param name="connector4"></param>
  335. /// <param name="context"></param>
  336. /// <returns></returns>
  337. public static FamilyInstance NewCrossFitting(Connector connector1, Connector connector2, Connector connector3, Connector connector4, JoinContext context)
  338. {
  339. if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected || connector3.IsConnected || connector4.IsConnected))
  340. {
  341. //已经连接部分
  342. return null;
  343. }
  344. Document doc = connector1.Owner.Document;
  345. List<Connector> inputConnectors = new List<Connector>() { connector1, connector2, connector3, connector4 };
  346. Connector baseConnector = connector1;
  347. Connector useConnector = null;
  348. for (int i = 1; i < inputConnectors.Count; i++)
  349. {
  350. if (!baseConnector.CoordinateSystem.BasisZ.IsParallel(inputConnectors[i].CoordinateSystem.BasisZ))
  351. {
  352. useConnector = inputConnectors[i];
  353. break;
  354. }
  355. }
  356. var line1 = Line.CreateUnbound(baseConnector.Origin, baseConnector.CoordinateSystem.BasisZ);
  357. var line3 = Line.CreateUnbound(useConnector.Origin, useConnector.CoordinateSystem.BasisZ);
  358. var location = line1.GetSpatialIntersection(line3);
  359. FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-四通");
  360. var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural);
  361. #region 根据样式,调整旋转
  362. FittingWrapper fitting = new FittingWrapper(box);
  363. var firstDirection = -line1.Direction;
  364. var secondDirection = -line3.Direction;
  365. var otherConnectors = inputConnectors.Where(c => c != baseConnector && c != useConnector).ToList();
  366. var crossDirection = firstDirection.CrossProduct(secondDirection);
  367. var comValue = -Math.Sqrt(2) / 2;//比较值,夹角cos值小于该值得,变换旋转方向
  368. if (otherConnectors.Any(c => (-c.CoordinateSystem.BasisZ).DotProduct(crossDirection).LessEqual(comValue)))
  369. {
  370. firstDirection = -firstDirection;
  371. }
  372. fitting.Rotate(location, firstDirection, secondDirection);
  373. #endregion
  374. var useMatchDirection = inputConnectors.Select(c => -c.CoordinateSystem.BasisZ).ToList();
  375. JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, useMatchDirection);
  376. AutoMatchJoin(box, inputConnectors, context);
  377. return box;
  378. }
  379. /// <summary>
  380. /// 创建五通连接
  381. /// </summary>
  382. /// <param name="connector1"></param>
  383. /// <param name="connector2"></param>
  384. /// <param name="connector3"></param>
  385. /// <param name="connector4"></param>
  386. /// <param name="connector5"></param>
  387. /// <param name="context"></param>
  388. /// <returns></returns>
  389. public static FamilyInstance NewFiveLinksFitting(Connector connector1, Connector connector2, Connector connector3, Connector connector4, Connector connector5, JoinContext context)
  390. {
  391. if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected || connector3.IsConnected || connector4.IsConnected || connector5.IsConnected))
  392. {
  393. //已经连接部分
  394. return null;
  395. }
  396. Document doc = connector1.Owner.Document;
  397. List<Connector> inputConnectors = new List<Connector>() { connector1, connector2, connector3, connector4, connector5 };
  398. Connector baseConnector = connector1;
  399. Connector useConnector = null;
  400. for (int i = 1; i < inputConnectors.Count; i++)
  401. {
  402. if (!baseConnector.CoordinateSystem.BasisZ.IsParallel(inputConnectors[i].CoordinateSystem.BasisZ))
  403. {
  404. useConnector = inputConnectors[i];
  405. break;
  406. }
  407. }
  408. var line1 = Line.CreateUnbound(baseConnector.Origin, baseConnector.CoordinateSystem.BasisZ);
  409. var line3 = Line.CreateUnbound(useConnector.Origin, useConnector.CoordinateSystem.BasisZ);
  410. var location = line1.GetSpatialIntersection(line3);
  411. FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-四通");
  412. var box = doc.Create.NewFamilyInstance(location, fs, StructuralType.NonStructural);
  413. #region 根据样式,调整旋转
  414. FittingWrapper fitting = new FittingWrapper(box);
  415. var firstDirection = -line1.Direction;
  416. var secondDirection = -line3.Direction;
  417. var otherConnectors = inputConnectors.Where(c => c != baseConnector && c != useConnector).ToList();
  418. var crossDirection = firstDirection.CrossProduct(secondDirection);
  419. var comValue = -Math.Sqrt(2) / 2;//比较值,夹角cos值小于该值得,变换旋转方向
  420. if (otherConnectors.Any(c => (-c.CoordinateSystem.BasisZ).DotProduct(crossDirection).LessEqual(comValue)))
  421. {
  422. firstDirection = -firstDirection;
  423. }
  424. fitting.Rotate(location, firstDirection, secondDirection);
  425. #endregion
  426. var useMatchDirection = inputConnectors.Select(c => -c.CoordinateSystem.BasisZ).ToList();
  427. JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, useMatchDirection);
  428. AutoMatchJoin(box, inputConnectors, context);
  429. return box;
  430. }
  431. }
  432. }