CreateSpaceCommand.cs 16 KB

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