|
@@ -0,0 +1,284 @@
|
|
|
|
+/*-------------------------------------------------------------------------
|
|
|
|
+ * 功能描述:创建空间外轮廓
|
|
|
|
+ * 作者:xulisong
|
|
|
|
+ * 创建时间: 2019/3/21 8:59:02
|
|
|
|
+ * 版本号:v1.0
|
|
|
|
+ * -------------------------------------------------------------------------*/
|
|
|
|
+
|
|
|
|
+using Autodesk.Revit.Attributes;
|
|
|
|
+using Autodesk.Revit.DB;
|
|
|
|
+using Autodesk.Revit.DB.Mechanical;
|
|
|
|
+using Autodesk.Revit.UI;
|
|
|
|
+using FWindSoft.Revit;
|
|
|
|
+using FWindSoft.Revit.Menu;
|
|
|
|
+using System;
|
|
|
|
+using System.Collections.Generic;
|
|
|
|
+using System.Linq;
|
|
|
|
+using System.Windows.Shapes;
|
|
|
|
+using FWindSoft.SystemExtensions;
|
|
|
|
+using SAGA.DotNetUtils.Extend;
|
|
|
|
+using SAGA.RevitUtils.Extends;
|
|
|
|
+using SAGA.RevitUtils.MEP;
|
|
|
|
+using ExternalCommand = FWindSoft.Revit.ExternalCommand;
|
|
|
|
+using Line = Autodesk.Revit.DB.Line;
|
|
|
|
+using DocumentExtension = FWindSoft.Revit.DocumentExtension;
|
|
|
|
+
|
|
|
|
+namespace LRH.Tool
|
|
|
|
+{
|
|
|
|
+ [Transaction(TransactionMode.Manual)]
|
|
|
|
+ [Regeneration(RegenerationOption.Manual)]
|
|
|
|
+ [Button(ButtonName = "创建空间轮廓线")]
|
|
|
|
+ public class CreateSpaceOutLineCommand : ExternalCommand
|
|
|
|
+ {
|
|
|
|
+ public override Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
|
|
|
|
+ {
|
|
|
|
+ using (Transaction tran = new Transaction(RevitCore.Doc, "创建空间"))
|
|
|
|
+ {
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ tran.Start();
|
|
|
|
+
|
|
|
|
+ var space = RevitCore.UIApp.PickElement("请选择空间", new CommonFilter(t => { return t is Space; })) as Space;
|
|
|
|
+ if (space == null) return Result.Cancelled;
|
|
|
|
+ SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
|
|
|
|
+ options.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.CoreBoundary;
|
|
|
|
+ var segments = space.GetBoundarySegments(options);
|
|
|
|
+
|
|
|
|
+ foreach (var segment in segments)
|
|
|
|
+ {
|
|
|
|
+ List<XYZ> xyzs = new List<XYZ>();
|
|
|
|
+ foreach (BoundarySegment boundarySegment in segment)
|
|
|
|
+ {
|
|
|
|
+ var wall = RevitCore.Doc.GetElement(boundarySegment.ElementId) as Wall;
|
|
|
|
+ if (wall == null) continue;
|
|
|
|
+ var wallCurve = ElementExtension.GetLocationCurve(wall);
|
|
|
|
+ var parallelWalls = GetParallelWalls(wall);
|
|
|
|
+ var segmentCurve = boundarySegment.GetCurve();
|
|
|
|
+ var tessellates = segmentCurve.Tessellate();
|
|
|
|
+ foreach (XYZ tessellate in tessellates)
|
|
|
|
+ {
|
|
|
|
+ xyzs.Add(tessellate);
|
|
|
|
+ //var project = wallCurve.GetProjectPt(tessellate);
|
|
|
|
+ //var verticalVector = (project - tessellate).Normalize();
|
|
|
|
+ //var tempXyz = GetOuterXYZ(tessellate, verticalVector, segmentCurve, parallelWalls);
|
|
|
|
+ //if (tempXyz != null)
|
|
|
|
+ // xyzs.Add(tempXyz);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //int count = xyzs.Count;
|
|
|
|
+ ///*
|
|
|
|
+ // * 相邻两条线求交点
|
|
|
|
+ // * 处理内拐角
|
|
|
|
+ // */
|
|
|
|
+ //for (int i = 0; i < count; i++)
|
|
|
|
+ //{
|
|
|
|
+ // var p1 = xyzs[i];
|
|
|
|
+ // int j2 = i + 1 >= xyzs.Count ? i + 1 - count : i + 1;
|
|
|
|
+ // var p2 = xyzs[j2];
|
|
|
|
+ // int j3 = i + 2 >= xyzs.Count ? i + 2 - count : i + 2;
|
|
|
|
+ // var p3 = xyzs[j3];
|
|
|
|
+ // int j4 = i + 3 >= xyzs.Count ? i + 3 - count : i + 3;
|
|
|
|
+ // var p4 = xyzs[j4];
|
|
|
|
+ // if (p1.IsEqual(p2) || p2.IsEqual(p3) || p3.IsEqual(p4))
|
|
|
|
+ // continue;
|
|
|
|
+ // Curve curve1 = Line.CreateBound(p1, p2);
|
|
|
|
+ // Curve curve2 = Line.CreateBound(p3, p4);
|
|
|
|
+ // XYZ intersection = curve1.GetIntersection(curve2);
|
|
|
|
+ // if (intersection == null) continue;
|
|
|
|
+ // xyzs[j2] = intersection;
|
|
|
|
+ // xyzs[j3] = intersection;
|
|
|
|
+ //}
|
|
|
|
+
|
|
|
|
+ if(xyzs.Any())
|
|
|
|
+ xyzs.Add(xyzs[0]);
|
|
|
|
+ //画线
|
|
|
|
+ for (int i = 0; i < xyzs.Count - 1; i++)
|
|
|
|
+ {
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ var p1 = xyzs[i];
|
|
|
|
+ var p2 = xyzs[i + 1];
|
|
|
|
+ if (p1.IsAlmostEqualTo(p2))
|
|
|
|
+ continue;
|
|
|
|
+ Curve curve = Line.CreateBound(p1, p2);
|
|
|
|
+ RevitCore.DocCreater.NewDetailCurve(RevitCore.UIDoc.ActiveView, curve);
|
|
|
|
+ }
|
|
|
|
+ catch (Exception e)
|
|
|
|
+ {
|
|
|
|
+ Console.WriteLine(e);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ tran.Commit();
|
|
|
|
+ }
|
|
|
|
+ catch (Exception e)
|
|
|
|
+ {
|
|
|
|
+ tran.RollBack();
|
|
|
|
+ throw;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return Result.Succeeded;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private double m_RedundancySpace = 10d;
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 获取平行墙,平行且投影有重叠部分
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="wall"></param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ public List<Wall> GetParallelWalls(Wall wall)
|
|
|
|
+ {
|
|
|
|
+ List<Wall> walls = new List<Wall>();
|
|
|
|
+ walls.Add(wall);
|
|
|
|
+ var doc = wall.Document;
|
|
|
|
+ var allWalls = DocumentExtension.GetElements<Wall>(doc);
|
|
|
|
+ var curve = ElementExtension.GetLocationCurve(wall);
|
|
|
|
+ var parallelWalls = allWalls.Where(t => ElementExtension.GetLocationCurve(t).IsParallel(curve));
|
|
|
|
+
|
|
|
|
+ int len = walls.Count;
|
|
|
|
+ do
|
|
|
|
+ {
|
|
|
|
+ len = walls.Count;
|
|
|
|
+ foreach (Wall parallelWall in parallelWalls)
|
|
|
|
+ {
|
|
|
|
+ var curve1 = ElementExtension.GetLocationCurve(parallelWall);
|
|
|
|
+ var startP11 = CurveExtend.StartPoint(curve1);
|
|
|
|
+ var endP11 = CurveExtend.EndPoint(curve1);
|
|
|
|
+ double width1 = parallelWall.Width.FromApi();
|
|
|
|
+ for (int i = 0; i < walls.Count; i++)
|
|
|
|
+ {
|
|
|
|
+ Wall referenceWall = walls[i];
|
|
|
|
+ if (walls.Contains(parallelWall)) continue;
|
|
|
|
+ var curve2 = ElementExtension.GetLocationCurve(referenceWall);
|
|
|
|
+ var startP12 = CurveExtend.GetProjectPt(curve2, startP11);
|
|
|
|
+ var endP12 = CurveExtend.GetProjectPt(curve2, endP11);
|
|
|
|
+ var startP21 = CurveExtend.StartPoint(curve2);
|
|
|
|
+ var endP21 = CurveExtend.EndPoint(curve2);
|
|
|
|
+ var startP22 = CurveExtend.GetProjectPt(curve1, startP21);
|
|
|
|
+ var endP22 = CurveExtend.GetProjectPt(curve1, endP21);
|
|
|
|
+ //没有重叠部分
|
|
|
|
+ if (startP12.IsOnCurve(curve2) || endP12.IsOnCurve(curve2) || startP22.IsOnCurve(curve1) ||
|
|
|
|
+ endP22.IsOnCurve(curve1))
|
|
|
|
+ {
|
|
|
|
+ double width2 = referenceWall.Width.FromApi();
|
|
|
|
+ double distacne = curve1.Distance(curve2).FromApi();
|
|
|
|
+ if (distacne < (width1 + width2) / 2.0 + m_RedundancySpace)
|
|
|
|
+ walls.Add(parallelWall);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } while (len != walls.Count);
|
|
|
|
+
|
|
|
|
+ return walls;
|
|
|
|
+ }
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 获取外廓后的点
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="xyz">外廓点</param>
|
|
|
|
+ /// <param name="verticalVector">外廓向量</param>
|
|
|
|
+ /// <param name="segmentCurve">参考Curve</param>
|
|
|
|
+ /// <param name="walls">平行墙集合</param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ public XYZ GetOuterXYZ(XYZ xyz, XYZ verticalVector, Curve segmentCurve, List<Wall> walls)
|
|
|
|
+ {
|
|
|
|
+ XYZ outerXyz = null;
|
|
|
|
+ double distance = 0;
|
|
|
|
+ foreach (Wall wall in walls)
|
|
|
|
+ {
|
|
|
|
+ List<PlanarFace> faces = GetBottomFaces(wall);
|
|
|
|
+ foreach (PlanarFace face in faces)
|
|
|
|
+ {
|
|
|
|
+ foreach (CurveLoop curveLoop in face.GetEdgesAsCurveLoops())
|
|
|
|
+ {
|
|
|
|
+ foreach (Curve curve in curveLoop)
|
|
|
|
+ {
|
|
|
|
+ /*
|
|
|
|
+ * 1. 获取平行边
|
|
|
|
+ * 2. 平行边求投影,投影点形成向量为向外
|
|
|
|
+ * 3. 投影点在线上
|
|
|
|
+ */
|
|
|
|
+ if (!segmentCurve.IsParallel(curve))
|
|
|
|
+ continue;
|
|
|
|
+ var projectPt = curve.GetProjectPt(xyz);
|
|
|
|
+ var tempVector = (projectPt - xyz).Normalize();
|
|
|
|
+ if (projectPt.IsOnCurve(curve) && !projectPt.IsEqual(xyz) && tempVector.IsEqual(verticalVector))
|
|
|
|
+ {
|
|
|
|
+ var tempDistance = xyz.DistanceTo(projectPt);
|
|
|
|
+ if (tempDistance >= distance)
|
|
|
|
+ {
|
|
|
|
+ distance = tempDistance;
|
|
|
|
+ outerXyz = projectPt;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ /*
|
|
|
|
+ * 无投影点,给个默认投影点的情况
|
|
|
|
+ * 处理斜边投影不到的情况边上的情况,
|
|
|
|
+ * 目前任意取了一个,多个取最近的??
|
|
|
|
+ */
|
|
|
|
+ if (outerXyz == null || outerXyz.IsEqual(xyz))
|
|
|
|
+ outerXyz = projectPt;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return outerXyz;
|
|
|
|
+ }
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 获取墙底面的轮廓,主要处理门窗分割墙问题
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="wall"></param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ public List<PlanarFace> GetBottomFaces(Wall wall)
|
|
|
|
+ {
|
|
|
|
+ var solids = wall.GetSolids();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ double volume = double.MinValue;
|
|
|
|
+ List<PlanarFace> faces = new List<PlanarFace>();
|
|
|
|
+ foreach (var solid in solids)
|
|
|
|
+ {
|
|
|
|
+ var tempVolume = solid.Volume;
|
|
|
|
+ double topZ = double.MaxValue;
|
|
|
|
+ //取体积最大的Solid
|
|
|
|
+ if (tempVolume.IsThan(volume))
|
|
|
|
+ {
|
|
|
|
+ volume = tempVolume;
|
|
|
|
+ faces.Clear();
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ foreach (Face face in solid.Faces)
|
|
|
|
+ {
|
|
|
|
+ if (face is PlanarFace planarFace && planarFace.FaceNormal.IsEqual(-XYZ.BasisZ))
|
|
|
|
+ {
|
|
|
|
+ var tempZ = planarFace.Origin.Z;
|
|
|
|
+ //取底面FaceNormal为(0,0,-1),Z最小
|
|
|
|
+ if (tempZ.IsLess(topZ))
|
|
|
|
+ {
|
|
|
|
+ faces.Clear();
|
|
|
|
+ topZ = tempZ;
|
|
|
|
+ faces.Add(planarFace);
|
|
|
|
+ }
|
|
|
|
+ else if (tempZ.IsEqual(topZ))
|
|
|
|
+ {
|
|
|
|
+ faces.Add(planarFace);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return faces;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+}
|