using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Mechanical; using Newtonsoft.Json.Linq; using SAGA.DotNetUtils.Extend; using SAGA.MBI.DataArrange; using SAGA.MBI.Model; using SAGA.MBI.RequestData; using SAGA.Models; using SAGA.RevitUtils; using SAGA.RevitUtils.Extends; using Point = System.Windows.Point; namespace SAGA.GplotRelationComputerManage { /// /// 空间关系 /// public class ReadSpaceCommand { List m_doors; List m_windows; private Dictionary> m_levelSpaceData; //存放所有层的楼层名称与所有空间边线 Dictionary /*边线*/>> allSpace = new Dictionary>>(); /// /// 缓存所有楼层的空间,避免重复加载数据 /// Dictionary /*空间*/> allSpaces = new Dictionary>(); /// /// 缓存所有转换后的空间对象 /// List allSgSpaces = new List(); private bool m_isDraw = false; List m_sgSpaces; private Dictionary m_dicServerSpace; List> m_connectionLine = new List>(); private Document m_doc; //需要进行上下比较房间类型 private List roomFunCtype = new List() { "140","150","1A0","1B0" }; /// /// 初始化数据集合 /// void Init() { m_doors = new List(); m_windows = new List(); m_levelSpaceData = new Dictionary>(); m_sgSpaces = new List(); } /// /// 向集合内添加空间,防止空间重复 /// /// public void AddSpaces(SgSpace sgSpace) { var space = m_sgSpaces.FirstOrDefault(t => t.Id == sgSpace.Id); if (sgSpace != null) { m_sgSpaces.Remove(space); } m_sgSpaces.Add(sgSpace); } /// /// 对所有模型进行空间计算 /// public void Computer() { //缓存所有空间数据 //查询所有设备 // var str = new string[] { "Si" };//元空间为Si //List datas = CommonConvert.QueryObjectInfoByTypes(str); //m_dicServerSpace = new Dictionary(); //datas.ForEach(t => //{ // var bimId = GetValue(t, "BIMID"); // if (!string.IsNullOrEmpty(bimId)) // { // m_dicServerSpace[bimId] = t; // } //}); //打开所有楼层数据 //Stopwatch watch = new Stopwatch(); //watch.Start(); var fileInfos = RevitModelPath.GetAllRevitFiles();//.Where(t => t.IndexOf("71062c") > -1); SpaceComputerContext context = new SpaceComputerContext(); foreach (var fileInfo in fileInfos) { if (File.Exists(fileInfo)) { //var uiApp = ExternalDataWrapper.Current.UiApp.Application; // var doc = uiApp.OpenDocumentFile(fileInfo); FloorSpaceItems floorItems = context.GetFloorItems(fileInfo); floorItems.Parse(context); floorItems.Document.Close(); //ComputerSpace(doc); } } //MessageShow.Infomation((watch.ElapsedMilliseconds / 1000).ToString()); //watch.Stop(); //MessageShow.Infomation((watch.ElapsedMilliseconds / 1000).ToString()); } /// /// 获取指定属性的值 /// /// /// /// public string GetValue(JObject equip, string propertyName) { // equip.GetValue("infos") string result = ""; if (equip != null) { result = equip["infos"][propertyName] + ""; } return result; } /// /// 读取Document里面的所有空间 /// /// /// /// List GetSpaces(Document doc, string filePath = "") { if (string.IsNullOrEmpty(filePath)) filePath = doc.PathName; var modelId = Path.GetFileNameWithoutExtension(filePath); Debug.Assert(modelId != null, nameof(modelId) + " != null"); if (allSpaces.ContainsKey(modelId)) { return allSpaces[modelId]; } if (doc == null) { if (File.Exists(filePath)) { var uiApp = ExternalDataWrapper.Current.UiApp.Application; doc = uiApp.OpenDocumentFile(filePath); } else return new List(); } List spaces = doc.GetAllElements().Where(e => e is Space space && !space.Area.IsZero()).Cast().ToList(); allSpaces.Add(modelId, spaces); return spaces; } /// /// 将Revit空间数据转换成可序列化的数据 /// /// /// /// SgSpace GetSgSpace(Space space, bool isCreate = true) { var sgSpace = allSgSpaces.FirstOrDefault(t => t.Id == space.Id.ToString()); if (sgSpace == null || !isCreate) { var floorId = space.Document.PathName.GetFileName(); var bimID = string.Format($"{floorId}:{space.Id.ToString()}"); var localName = space.Name; var spaceType = string.Empty; m_dicServerSpace.TryGetValue(bimID, out JObject jo); if (jo != null) { localName = GetValue(jo, "RoomLocalName"); spaceType=GetValue(jo, "RoomFuncType"); } sgSpace = new SgSpace() { Id = space.Id.ToString(), Name =string.IsNullOrEmpty(localName)? space.Name: localName, Point = space.Location.GetPoint().ToW2DPoint(), // Point = GetCenter(space).ToW2DPoint(), RoomFuncType = spaceType, BoundarySegment = ConvertBoundarySegmentTo2D(new LvSpace(space)) }; if (isCreate) allSgSpaces.Add(sgSpace); return sgSpace; } return sgSpace; } /// /// 计算空间关系 /// /// public void ComputerSpace(Document doc) { m_doc = doc; Init(); m_doors = doc.GetElements(BuiltInCategory.OST_Doors); m_windows = doc.GetElements(BuiltInCategory.OST_Windows); List spaces = GetSpaces(doc); #region 计算平面拓扑 System.Diagnostics.Debug.WriteLine(DateTime.Now + "平面计算开始"); //转换所有空间 var dataSpaces = ConvertSpace(spaces); //根据边上的Element分组 var groupSpaces = dataSpaces.GroupBy(t => t.EdgeElement.Id.IntegerValue).ToList(); //合并公共构建,如果是面层墙则进行合并 var datas = CombinElement(groupSpaces); foreach (var groupSpace in datas) { var lSpaces = groupSpace.Value; var count = lSpaces.Count; if (count > 1) { for (int i = 0; i < count; i++) { var ls1 = lSpaces[i]; var mySpace = ls1.Space; SgSpace currSgSpace = GetSgSpace(mySpace); AddSpaces(currSgSpace); for (int j = i + 1; j < count; j++) { var ls2 = lSpaces[j]; var space = ls2.Space; if (mySpace.Id.IsEqual(space.Id)) continue; var sgSpace = GetSgSpace(space, false); //如果关系已经存在 if ((sgSpace.AdjacentSpaces.Any(t => t.Space.Id == currSgSpace.Id) || currSgSpace.AdjacentSpaces.Any(t => t.Space.Id == sgSpace.Id))) continue; //分别做投影 Curve curve1 = GetProjectLine(ls1.Edge, ls1.SegEdge); Curve curve2 = GetProjectLine(ls1.Edge, ls2.SegEdge); //判断投影是否相交或者包含 //var isConn = curve2.Intersect(curve1) != SetComparisonResult.Disjoint; IntersectionResultArray ira; var isConn = curve2.Intersect(curve1, out ira) != SetComparisonResult.Disjoint; if (ira != null && ira.Size == 1) continue; var isVirtual = ls1.EType == typeof(ModelCurve); if (isConn) { //判断关系 var spaceRelated = isVirtual ? SpaceRelatedEnum.All : JudgeDoorAndWindow(ls1.EdgeElement, curve1, curve2); var adjacentSpace = new AdjacentSpace() { Space = sgSpace, RelatedEnum = spaceRelated }; currSgSpace.AdjacentSpaces.Add(adjacentSpace); m_connectionLine.Add(new List() { currSgSpace.Point, sgSpace.Point }); } } } } } #endregion System.Diagnostics.Debug.WriteLine(DateTime.Now + "平面计算结束"); var name = doc.Title; //上层模型名字 var upFloor = GetUpFloorKey(name); if (upFloor != null) { var upSpaces = GetSpaces(null, upFloor.FullPath); var upLvSpaces = upSpaces.Select(t => new LvSpace(t)).ToList(); var currLvSpaces = spaces.Select(t => new LvSpace(t)); foreach (var lvSpace in currLvSpaces) { ComputerUpDownRelationship(lvSpace, upLvSpaces); } //判断上一层是否为夹层 if (upFloor.FloorType == "4") //夹层为4 { //取出夹层的上一层 var upUpFloor = GetUpFloorKey(upFloor.FullPath); if (upUpFloor != null) { var upUpSpaces = GetSpaces(null, upUpFloor.FullPath); var upUpLvSpaces = upUpSpaces.Select(t => new LvSpace(t)).ToList(); foreach (var lvSpace in currLvSpaces.Where(t => t.IsPaired == false)) { ComputerUpDownRelationship(lvSpace, upUpLvSpaces); } } } } //保存数据到数据库 var id = Regex.Replace(doc.PathName, @".+\\(\w+)\\(\w+).rvt$", "$1-$2"); var saveName = DalProjectTree.GetFloorNameByFloorId(id.Split('-')[1]); var levels = doc.GetLevels(); string levelName = saveName; var currLevels = levels.FirstOrDefault(t => t.Name.IndexOf("-saga") > -1); if (currLevels != null) levelName = currLevels.Name; FloorSpaceData floorData = new FloorSpaceData { Name = saveName, Id = id, LevelName = levelName.Replace("-saga", "") }; m_sgSpaces.ForEach(s => floorData.EndPoints.Add(s)); //获取标高名称,并按高度排序 List levelNames = levels.OrderBy(t => t.Elevation).Where(t => Regex.IsMatch(t.Name, "[F|B]")).Select(t => t.Name.Replace("-saga", "")).ToList(); DrawDataServer.SaveSpaceFloorData(new List() { floorData }, levelNames); //换成楼层空间数据 if (m_levelSpaceData.Count > 0 && !allSpace.ContainsKey(name)) allSpace.Add(name, m_levelSpaceData); } /// /// 合并公共构件 /// /// /// private List>> CombinElement(List> eleDatas) { var datas = eleDatas.ToDictionary(t => t.Key, t => t.ToList()).ToList(); for (int i = datas.Count - 1; i >= 0; i--) { var d1 = datas[i]; var e1 = d1.Value.FirstOrDefault().EdgeElement; var thickness1 = 0d; var line1 = GetElementLine(e1, out thickness1); if(line1==null)continue; var middle = line1.MiddlePoint(); for (int j = i - 1; j >= 0; j--) { j = Math.Min(datas.Count-1, j); var d2 = datas[j]; if (d2.Key == d1.Key) continue; var e2 = d2.Value.FirstOrDefault().EdgeElement; var thickness2 = 0d; var line2 = GetElementLine(e2, out thickness2); if(line2==null||line1==null)continue; if (line1.IsParallel(line2))//平行 { //间距小于两个厚度之和的一半 var project = line2.GetProjectPt(middle); var length = (middle - project).GetLength(); if (((thickness1 + thickness2) / 2.0d).IsThanEq(length)) { //将线投影到另外一条线上,两条线必须共线 var projectLine = GetProjectLine(line1, line2); if (projectLine.Intersect(line1) != SetComparisonResult.Disjoint) { //合并两个Element,取长的保留 if (line1.Length < line2.Length) { d2.Value.AddRange(d1.Value); datas.RemoveAll(t => t.Key == (d1.Key)); } else { d1.Value.AddRange(d2.Value); datas.RemoveAll(t => t.Key == (d2.Key)); } } } } } } return datas; } /// /// 获取Element的厚度以及中轴线 /// /// /// /// Line GetElementLine(Element ele, out double width) { width = 0; if (ele is Wall wall) { width = wall.WallType.GetParameterDouble(BuiltInParameter.WALL_ATTR_WIDTH_PARAM); } return ele.GetLocationCurve().GetLine(); } /// /// 获取Curve1在Curve2上的投影 /// /// /// /// private Curve GetProjectLine(Curve projectLine, Curve line) { var startPoint = projectLine.GetProjectPt(line.StartPoint()); var endPoint = projectLine.GetProjectPt(line.EndPoint()); if (projectLine is Arc) { var middle = projectLine.GetProjectPt((startPoint + endPoint) / 2); return Arc.Create(startPoint, endPoint, middle); } return Line.CreateBound(startPoint, endPoint); } /// /// 判断当前两个空间是否有门窗 /// /// /// /// /// private SpaceRelatedEnum JudgeDoorAndWindow(Element wall, Curve curve1, Curve curve2) { var wallId = wall.Id; var wallWidth = (wall as Wall)?.WallType.Width; var z = curve1.StartPoint().Z; SpaceRelatedEnum spaceRelated = SpaceRelatedEnum.Adjacent; var doors = m_doors.Where(t => t.Host.Id.IsEqual(wallId)); var windows = m_windows.Where(t => t.Host.Id.IsEqual(wallId)); Predicate isHost = (point) => { point = point.NewZ(z); //向两个线上做投影 var point1 = curve1.GetProjectPt(point, false); var point2 = curve2.GetProjectPt(point, false); //定位点到两个投影的必须小于墙厚 return (point - point1).GetLength() < wallWidth && (point - point2).GetLength() < wallWidth; }; if (doors.Any(t => isHost(t.GetLocationPoint()))) spaceRelated = spaceRelated | SpaceRelatedEnum.Crossing | SpaceRelatedEnum.Ventilation; if (windows.Any(t => isHost(t.GetLocationPoint()))) spaceRelated = spaceRelated | SpaceRelatedEnum.Radiation | SpaceRelatedEnum.Ventilation; return spaceRelated; } /// /// 计算上层相关联的空间 /// /// /// void ComputerUpDownRelationship(LvSpace space, List upSpaces) { var curves = space.ZeroCurves; var sgSpace = m_sgSpaces.FirstOrDefault(t => t.Id == space.Space.Id.ToString()) ?? GetSgSpace(space.Space, false); foreach (var upSpace in upSpaces) { if (ClosedIntersect(upSpace.ZeroCurves, curves)) { space.IsPaired = true; var s = GetSgSpace(upSpace.Space, false); var related = SpaceRelatedEnum.Adjacent; //判断上下关系 //TODO:此处取出空间类型属性,如果一致则建立关系,此处交通必须是一对一,如果不是,则不建立关系(1对多不建立关系) if (s.RoomFuncType == sgSpace.RoomFuncType && roomFunCtype.Contains(s.RoomFuncType)) { related = related | SpaceRelatedEnum.Crossing; space.UpLvSpaces.Add(upSpace); } sgSpace.UpElements.Add(new AdjacentSpace() { Space = s, RelatedEnum = related }); } } } /// /// 将空间轮廓转换成Win2d /// /// /// private List> ConvertBoundarySegmentTo2D(LvSpace upSpace) { //取出边缘 var datas = new List>(); foreach (var curve in upSpace.ZeroCurves) { if (curve is Autodesk.Revit.DB.Line line) { // var line = segment.GetCurve().GetLine(); datas.Add(new List(){ line.GetEndPoint(0).ToW2DPoint() ,line.GetEndPoint(1).ToW2DPoint() }); } else if (curve is Autodesk.Revit.DB.Arc arc) { datas.Add(arc.Tessellate().Select(xyz => xyz.ToW2DPoint()).ToList()); } } return datas; } /// /// 判断两个区域是否相交或者相互包含 /// /// /// /// public bool ClosedIntersect(List closed1, List closed2) { //判断所有的线是否有相交 foreach (var curve1 in closed1) { if (curve1.IsIntersectPoly(closed2)) return true; } //判断是否包含 foreach (var curve in closed1) { if (curve.StartPoint().PointInPolygon(closed2)) return true; } return closed2.Any(curve => curve.StartPoint().PointInPolygon(closed1)); } /// /// 获取上层空间信息 /// /// 楼层名称 /// 上层楼层名称 MFloor GetUpFloorKey(string key) { var modelName = System.IO.Path.GetFileNameWithoutExtension(key); MFloor floor = DalProjectTree.GetUpperFloor(modelName); return floor; } /// /// 将所有空间转换成边集合对象 /// /// /// private List ConvertSpace(List spaces) { var lSpaces = new List(); foreach (var space in spaces) { //如果空间内有多个柱子,每个柱子相当于一个封闭区域,也是一个List,如果一个空间分隔符或者墙上有一个柱子,所在的空间轮廓就会多出两个边分别是柱子左边,柱子,柱子右边,他们三个的ElementId相同,getCurve不同 IList> segments = space.GetBoundarySegments(new SpatialElementBoundaryOptions()); foreach (IList boundarySegments in segments) { foreach (var segment in boundarySegments) { //去除不能构成边的元素 if (segment.ElementId.IntegerValue != -1) { Curve wallLine = null; var elem = m_doc.GetElement(segment.ElementId); var type = typeof(object); //这里只处理墙和分隔符 if (elem is Wall wall) { wallLine = wall.Location.GetCurve(); type = typeof(Wall); } else if (elem is ModelCurve modelCurve) { wallLine = modelCurve.GetCurve(); type = typeof(ModelCurve); } else { continue; } Curve segCurve1 = segment.GetCurve(); lSpaces.Add(new SpaceEdgeInfo() { Space = space, Edge = wallLine, SegEdge = segCurve1, EdgeElement = elem, EType = type }); } } } // var edgeCopy=Array.Copy() } return lSpaces; } } /// /// 比较轮廓边是否相等 /// public class Comparea : IEqualityComparer { public bool Equals(SpaceEdgeInfo x, SpaceEdgeInfo y) { return x.Space.Id.IsEqual(y.Space.Id); } public int GetHashCode(SpaceEdgeInfo obj) { return 0; } } }