CreateSpaceCommand.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*-------------------------------------------------------------------------
  2. * 功能描述:创建空间外轮廓
  3. * 作者:xulisong
  4. * 创建时间: 2019/3/21 8:59:02
  5. * 版本号:v1.0
  6. * -------------------------------------------------------------------------*/
  7. using Autodesk.Revit.Attributes;
  8. using Autodesk.Revit.DB;
  9. using Autodesk.Revit.DB.Mechanical;
  10. using Autodesk.Revit.UI;
  11. using FWindSoft.Revit;
  12. using FWindSoft.Revit.Menu;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. using System.Windows.Shapes;
  17. using FWindSoft.SystemExtensions;
  18. using SAGA.DotNetUtils.Extend;
  19. using SAGA.RevitUtils.Extends;
  20. using SAGA.RevitUtils.MEP;
  21. using ExternalCommand = FWindSoft.Revit.ExternalCommand;
  22. using Line = Autodesk.Revit.DB.Line;
  23. using DocumentExtension = FWindSoft.Revit.DocumentExtension;
  24. namespace LRH.Tool
  25. {
  26. [Transaction(TransactionMode.Manual)]
  27. [Regeneration(RegenerationOption.Manual)]
  28. [Button(ButtonName = "创建空间轮廓线")]
  29. public class CreateSpaceOutLineCommand : ExternalCommand
  30. {
  31. public override Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
  32. {
  33. using (Transaction tran = new Transaction(RevitCore.Doc, "创建空间"))
  34. {
  35. try
  36. {
  37. tran.Start();
  38. //var space = RevitCore.UIApp.PickElement("请选择空间", new CommonFilter(t => { return t is Space; })) as Space;
  39. var space = RevitCore.UIApp.GetSelectedElement() as Space;
  40. if (space == null) return Result.Cancelled;
  41. SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
  42. options.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Center;
  43. var segments = space.GetBoundarySegments(options);
  44. double tolerance = RevitCore.App.ShortCurveTolerance;
  45. foreach (var segment in segments)
  46. {
  47. List<XYZ> xyzs = new List<XYZ>();
  48. List<XYZ> xyzs2 = new List<XYZ>();
  49. foreach (BoundarySegment boundarySegment in segment)
  50. {
  51. var segmentElement = RevitCore.Doc.GetElement(boundarySegment.ElementId);
  52. if (segmentElement is FamilyInstance fi)
  53. {
  54. foreach (XYZ tessellate in boundarySegment.GetCurve().Tessellate())
  55. {
  56. xyzs.Add(tessellate);
  57. }
  58. //var point = ElementExtension.GetLocationPoint(fi);
  59. //if(point!=null)
  60. // xyzs.Add(point);
  61. }
  62. else if(segmentElement is Wall wall)
  63. {
  64. var wallCurve = ElementExtension.GetLocationCurve(wall);
  65. var parallelWalls = GetParallelWalls(wall);
  66. var segmentCurve = boundarySegment.GetCurve();
  67. var tessellates = segmentCurve.Tessellate();
  68. foreach (XYZ tessellate in tessellates)
  69. {
  70. xyzs2.Add(tessellate);
  71. var project = wallCurve.GetProjectPt(tessellate);
  72. var verticalVector = (project - tessellate).Normalize();
  73. var tempXyz = GetOuterXYZ(tessellate, verticalVector, segmentCurve, parallelWalls);
  74. if (tempXyz != null)
  75. xyzs.Add(tempXyz);
  76. }
  77. }
  78. else
  79. {
  80. foreach (XYZ tessellate in boundarySegment.GetCurve().Tessellate())
  81. {
  82. xyzs2.Add(tessellate);
  83. xyzs.Add(tessellate);
  84. }
  85. }
  86. }
  87. //int count = xyzs.Count;
  88. ///*
  89. // * 相邻两条线求交点
  90. // * 处理内拐角
  91. // */
  92. //for (int i = 0; i < count; i++)
  93. //{
  94. // if (count < 4) continue;
  95. // var p1 = xyzs[i];
  96. // int j2 = i + 1 >= xyzs.Count ? i + 1 - count : i + 1;
  97. // var p2 = xyzs[j2];
  98. // int j3 = i + 2 >= xyzs.Count ? i + 2 - count : i + 2;
  99. // var p3 = xyzs[j3];
  100. // int j4 = i + 3 >= xyzs.Count ? i + 3 - count : i + 3;
  101. // var p4 = xyzs[j4];
  102. // if (p2.IsEqual(p3) || p1.DistanceTo(p2) < tolerance || p3.DistanceTo(p4) < tolerance)
  103. // continue;
  104. // Curve curve1 = Line.CreateBound(p1, p2);
  105. // Curve curve2 = Line.CreateBound(p3, p4);
  106. // XYZ intersection = curve1.GetIntersection(curve2);
  107. // if (intersection == null) continue;
  108. // xyzs[j2] = intersection;
  109. // xyzs[j3] = intersection;
  110. //}
  111. //if (xyzs.Any())
  112. // xyzs.Add(xyzs[0]);
  113. ////画线-外墙
  114. //for (int i = 0; i < xyzs.Count - 1; i++)
  115. //{
  116. // try
  117. // {
  118. // var p1 = xyzs[i];
  119. // var p2 = xyzs[i + 1];
  120. // if (p1.IsAlmostEqualTo(p2)||p1.DistanceTo(p2)<tolerance)
  121. // continue;
  122. // Curve curve = Line.CreateBound(p1, p2);
  123. // RevitCore.DocCreater.NewDetailCurve(RevitCore.UIDoc.ActiveView, curve);
  124. // }
  125. // catch (Exception e)
  126. // {
  127. // Console.WriteLine(e);
  128. // }
  129. //}
  130. if (xyzs2.Any())
  131. xyzs2.Add(xyzs2[0]);
  132. //画线-内墙
  133. for (int i = 0; i < xyzs2.Count - 1; i++)
  134. {
  135. try
  136. {
  137. var p1 = xyzs2[i];
  138. var p2 = xyzs2[i + 1];
  139. if (p1.IsAlmostEqualTo(p2) || p1.DistanceTo(p2) < tolerance)
  140. continue;
  141. Curve curve = Line.CreateBound(p1, p2);
  142. RevitCore.DocCreater.NewDetailCurve(RevitCore.UIDoc.ActiveView, curve);
  143. }
  144. catch (Exception e)
  145. {
  146. Console.WriteLine(e);
  147. }
  148. }
  149. }
  150. tran.Commit();
  151. }
  152. catch (Exception e)
  153. {
  154. tran.RollBack();
  155. throw;
  156. }
  157. }
  158. return Result.Succeeded;
  159. }
  160. private double m_RedundancySpace = 10d;
  161. /// <summary>
  162. /// 获取平行墙,平行且投影有重叠部分
  163. /// </summary>
  164. /// <param name="wall"></param>
  165. /// <returns></returns>
  166. public List<Wall> GetParallelWalls(Wall wall)
  167. {
  168. List<Wall> walls = new List<Wall>();
  169. walls.Add(wall);
  170. var doc = wall.Document;
  171. var allWalls = DocumentExtension.GetElements<Wall>(doc);
  172. var curve = ElementExtension.GetLocationCurve(wall);
  173. var parallelWalls = allWalls.Where(t => ElementExtension.GetLocationCurve(t).IsParallel(curve));
  174. int len = walls.Count;
  175. do
  176. {
  177. len = walls.Count;
  178. foreach (Wall parallelWall in parallelWalls)
  179. {
  180. var curve1 = ElementExtension.GetLocationCurve(parallelWall);
  181. var startP11 = CurveExtend.StartPoint(curve1);
  182. var endP11 = CurveExtend.EndPoint(curve1);
  183. double width1 = parallelWall.Width.FromApi();
  184. for (int i = 0; i < walls.Count; i++)
  185. {
  186. Wall referenceWall = walls[i];
  187. if (walls.Contains(parallelWall)) continue;
  188. var curve2 = ElementExtension.GetLocationCurve(referenceWall);
  189. var startP12 = CurveExtend.GetProjectPt(curve2, startP11);
  190. var endP12 = CurveExtend.GetProjectPt(curve2, endP11);
  191. var startP21 = CurveExtend.StartPoint(curve2);
  192. var endP21 = CurveExtend.EndPoint(curve2);
  193. var startP22 = CurveExtend.GetProjectPt(curve1, startP21);
  194. var endP22 = CurveExtend.GetProjectPt(curve1, endP21);
  195. //没有重叠部分
  196. if (startP12.IsOnCurve(curve2) || endP12.IsOnCurve(curve2) || startP22.IsOnCurve(curve1) ||
  197. endP22.IsOnCurve(curve1))
  198. {
  199. double width2 = referenceWall.Width.FromApi();
  200. double distacne = curve1.Distance(curve2).FromApi();
  201. if (distacne < (width1 + width2) / 2.0 + m_RedundancySpace)
  202. walls.Add(parallelWall);
  203. }
  204. }
  205. }
  206. } while (len != walls.Count);
  207. return walls;
  208. }
  209. /// <summary>
  210. /// 获取外廓后的点
  211. /// </summary>
  212. /// <param name="xyz">外廓点</param>
  213. /// <param name="verticalVector">外廓向量</param>
  214. /// <param name="segmentCurve">参考Curve</param>
  215. /// <param name="walls">平行墙集合</param>
  216. /// <returns></returns>
  217. public XYZ GetOuterXYZ(XYZ xyz, XYZ verticalVector, Curve segmentCurve, List<Wall> walls)
  218. {
  219. XYZ outerXyz = xyz;
  220. double distance = 0;
  221. foreach (Wall wall in walls)
  222. {
  223. /*
  224. * 注意幕墙,没有底面,直接取原始点xyz
  225. */
  226. List<PlanarFace> faces = GetBottomFaces(wall);
  227. foreach (PlanarFace face in faces)
  228. {
  229. foreach (CurveLoop curveLoop in face.GetEdgesAsCurveLoops())
  230. {
  231. foreach (Curve curve in curveLoop)
  232. {
  233. /*
  234. * 1. 获取平行边
  235. * 2. 平行边求投影,投影点形成向量为向外
  236. * 3. 投影点在线上
  237. */
  238. if (!segmentCurve.IsParallel(curve))
  239. continue;
  240. var projectPt = curve.GetProjectPt(xyz);
  241. var tempVector = (projectPt - xyz).Normalize();
  242. if (projectPt.IsOnCurve(curve) && !projectPt.IsEqual(xyz) && tempVector.IsEqual(verticalVector))
  243. {
  244. var tempDistance = xyz.DistanceTo(projectPt);
  245. if (tempDistance >= distance)
  246. {
  247. distance = tempDistance;
  248. outerXyz = projectPt;
  249. }
  250. }
  251. else
  252. {
  253. /*
  254. * 无投影点,给个默认投影点的情况
  255. * 处理斜边投影不到的情况边上的情况,
  256. * 目前任意取了一个,多个取最近的??
  257. */
  258. if (outerXyz == null || outerXyz.IsEqual(xyz))
  259. outerXyz = projectPt;
  260. }
  261. }
  262. }
  263. }
  264. }
  265. return outerXyz;
  266. }
  267. /// <summary>
  268. /// 获取墙底面的轮廓,主要处理门窗分割墙问题
  269. /// </summary>
  270. /// <param name="wall"></param>
  271. /// <returns></returns>
  272. public List<PlanarFace> GetBottomFaces(Wall wall)
  273. {
  274. var solids = wall.GetSolids();
  275. double volume = double.MinValue;
  276. List<PlanarFace> faces = new List<PlanarFace>();
  277. foreach (var solid in solids)
  278. {
  279. var tempVolume = solid.Volume;
  280. double topZ = double.MaxValue;
  281. //取体积最大的Solid
  282. if (tempVolume.IsThan(volume))
  283. {
  284. volume = tempVolume;
  285. faces.Clear();
  286. }
  287. else
  288. {
  289. continue;
  290. }
  291. foreach (Face face in solid.Faces)
  292. {
  293. if (face is PlanarFace planarFace && planarFace.FaceNormal.IsEqual(-XYZ.BasisZ))
  294. {
  295. var tempZ = planarFace.Origin.Z;
  296. //取底面FaceNormal为(0,0,-1),Z最小
  297. if (tempZ.IsLess(topZ))
  298. {
  299. faces.Clear();
  300. topZ = tempZ;
  301. faces.Add(planarFace);
  302. }
  303. else if (tempZ.IsEqual(topZ))
  304. {
  305. faces.Add(planarFace);
  306. }
  307. }
  308. }
  309. }
  310. return faces;
  311. }
  312. }
  313. }