using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows; using Bentley.DgnPlatformNET; using Bentley.DgnPlatformNET.DgnEC; using Bentley.DgnPlatformNET.Elements; using Bentley.ECObjects.Instance; using Bentley.ECObjects.Schema; using Bentley.GeometryNET; using Bentley.MstnPlatformNET; using ClrCode; namespace CenterCurveAddins { /// /// 凸包算法 /// internal class ConvexHull { public static HashSet Compute(HashSet DPoint3ds) { if (DPoint3ds.Count <= 1) return new HashSet(DPoint3ds); // 按x升序排列,x相同则按y升序 var sortedDPoint3ds = DPoint3ds.OrderBy(p => p.X).ThenBy(p => p.Y).ToList(); List lowerHull = new List(); // 构造下凸包 foreach (var DPoint3d in sortedDPoint3ds) { while (lowerHull.Count >= 2 && Cross(lowerHull[lowerHull.Count - 2], lowerHull[lowerHull.Count - 1], DPoint3d) <= 0) { lowerHull.RemoveAt(lowerHull.Count - 1); } lowerHull.Add(DPoint3d); } List upperHull = new List(); // 构造上凸包,逆序处理 for (int i = sortedDPoint3ds.Count - 1; i >= 0; i--) { DPoint3d DPoint3d = sortedDPoint3ds[i]; while (upperHull.Count >= 2 && Cross(upperHull[upperHull.Count - 2], upperHull[upperHull.Count - 1], DPoint3d) <= 0) { upperHull.RemoveAt(upperHull.Count - 1); } upperHull.Add(DPoint3d); } // 合并上下凸包,并移除重复点 lowerHull.AddRange(upperHull); if (lowerHull.Count > 0) lowerHull.RemoveAt(lowerHull.Count - 1); // 移除上凸包的第一个点(与下凸包最后一个点重复) return new HashSet(lowerHull); } // 计算叉积:(b - a) × (c - b) private static double Cross(DPoint3d a, DPoint3d b, DPoint3d c) { return (b.X - a.X) * (c.Y - b.Y) - (b.Y - a.Y) * (c.X - b.X); } } /// /// 隧道网格投影到中线的帮助类 /// public class TunnelLengthHelper { /// /// 分割具有间隙的网格,引用算量源码的方法 /// /// /// 可拆分后的网格,或不可拆分的原网格 /// private static bool DivMesh(Element eleMeshDiv, out List listEleId) { listEleId = new List(); int[] iArrMeshDivide = { };//元素id bool bRet = ClrCode.GeneralToolClr.DivideMesh(eleMeshDiv.ElementId.GetHashCode(), ref iArrMeshDivide); if (bRet) { if (iArrMeshDivide.Count() > 1)//如果拆分后的数量>1,则证明原曲面与拆分后曲面是不一样的 { foreach (var iEle in iArrMeshDivide) { listEleId.Add(iEle); ulong lEle = (ulong)iEle; var id = new ElementId(ref lEle); var elem = Session.Instance.GetActiveDgnModel().FindElementById(id); DeliverItemTypesTo(eleMeshDiv, elem); } //删除原始元素 eleMeshDiv?.DeleteFromModel(); return true; } else { listEleId.Add((int)eleMeshDiv.ElementId); foreach (var iEle in iArrMeshDivide) { ulong lEle = (ulong)iEle; var id = new ElementId(ref lEle); var elem = Session.Instance.GetActiveDgnModel().FindElementById(id); elem?.DeleteFromModel(); } } } return false; } /// /// 获取网格投影到XOY平面的边界点 /// /// /// private static HashSet GetBoundariesFromMeshByProject(Element eleMesh) { var listPtNew = new HashSet(); try { long lElementIDProject = 0; //平面投影 bool bRet = GeneralToolClr.ProjectMeshToXY(eleMesh.ElementId.GetHashCode(), ref lElementIDProject); if (!bRet) { return listPtNew; } //平面投影元素 ElementId eleIdProject = new ElementId(ref lElementIDProject); Element eleProject = Session.Instance.GetActiveDgnModel().FindElementById(eleIdProject); if (eleProject == null) { return null; } ChildElementEnumerator enumeratorPro = new ChildElementEnumerator(eleProject); while (enumeratorPro.MoveNext()) { ShapeElement shapeEle = enumeratorPro.Current as ShapeElement; if (shapeEle == null) { MessageCenter.Instance.ShowInfoMessage($"shapeEle:{shapeEle.ElementId.GetHashCode()}", "未找到子元素ShapeElement", false); continue; } CurveVector curVector = shapeEle.GetCurveVector(); if (curVector == null) { MessageCenter.Instance.ShowInfoMessage($"shapeEle:{shapeEle.ElementId.GetHashCode()}", "未找到ShapeElement的边线", false); } foreach (var itemCur in curVector) { if (itemCur.GetCurvePrimitiveType() == CurvePrimitive.CurvePrimitiveType.LineString) { var pts = new List(); if (itemCur.TryGetLineString(pts) && pts.Count > 2) { foreach (var item in pts) { listPtNew.Add(item); } //listPtNew = new HashSet(pts); } else { MessageCenter.Instance.ShowInfoMessage($"shapeEle:{shapeEle.ElementId.GetHashCode()}, pts:{pts.Count}", "", false); } } } } listPtNew = ConvexHull.Compute(listPtNew); eleProject.DeleteFromModel(); return listPtNew; } catch (Exception) { return listPtNew; } } /// /// 通过点分布获取中心线 /// /// 线元素 /// 拍平后的所有平面点 /// private static Element GetCenterLine(List listLineMilGeoData, HashSet listPtAll) { Element targetLine = null; //随机取点 if (listPtAll == null || listPtAll.Count == 0) { return targetLine; } var pt3dMid = listPtAll.FirstOrDefault(); double dDisMin = double.MaxValue; //找到距离点最近的线元素 foreach (var item in listLineMilGeoData) { CurveVector cv = CurvePathQuery.ElementToCurveVector(item); if (cv == null) { continue; } //平面距离 CurveLocationDetail curLocDetail = cv.ClosestPointBoundedXY(pt3dMid); if (curLocDetail == null) { continue; } //平面距离 double dDis = pt3dMid.DistanceXY(curLocDetail.Point); if (dDis < dDisMin) { dDisMin = dDis; targetLine = item; } } return targetLine; } /// /// 获取网格投影在最近的中线投影长度 /// /// mesh实体 /// 中心线所在图层,如DG-ZZX-ZX /// private static double GetMeshLength(MeshHeaderElement meshHeaderEle, string centerLineLevel) { if (meshHeaderEle == null) { return 0.0; } DgnModel dgnModel = Session.Instance.GetActiveDgnModel(); var levelCache = dgnModel.GetLevelCache(); //过滤获取线条元素,即所有中心线 var listLineMilGeoData = dgnModel.GetGraphicElements() .Where( e => e.IsValid && !e.IsInvisible && levelCache.GetLevel(e.LevelId)?.DisplayName.StartsWith(centerLineLevel) == true && IsCurve(e)) .ToList(); //曲面拍平的所有点 HashSet listPtAll = GetBoundariesFromMeshByProject(meshHeaderEle); //通过点分布获取中心线 var targetLine = GetCenterLine(listLineMilGeoData, listPtAll); if (targetLine != null) { CurveVector curveVector = CurvePathQuery.ElementToCurveVector(targetLine); //找到曲面拍平的 所有点 到 平面距离最近中心线上的点 var locs = listPtAll.Select(curveVector.ClosestPointBoundedXY); //StringBuilder sb = new StringBuilder(); //foreach (var item in locs) //{ // sb.AppendLine($"{item.Point}"); //} //File.WriteAllText( // Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "结果.txt"), // sb.ToString()); //locs = locs.Distinct((p1, p2) => p1.Point.Equals(p2.Point)); //var minFraction = locs.Min(loc => loc.Fraction); //var maxFraction = locs.Max(loc => loc.Fraction); //foreach (var item in locs) //{ // //var z = item.Point + DPoint3d.UnitZ * 100000; // var p = curveVector.ClosestPointBoundedXY(item.Point); // var pointString = new LineStringElement(dgnModel, null, new DPoint3d[2] { item.Point, p.Point }); // pointString.AddToModel(); //} var ordered = locs.OrderBy(loc => curveVector.CurveLocationDetailIndex(loc)).ThenBy(loc => loc.Fraction); var min = ordered.FirstOrDefault(); var max = ordered.LastOrDefault(); var minIndex = curveVector.CurveLocationDetailIndex(min); var maxIndex = curveVector.CurveLocationDetailIndex(max); var minCurve = curveVector.GetPrimitive((int)minIndex); var maxCurve = curveVector.GetPrimitive((int)maxIndex); //拿到最大最小线段距离 minCurve.SignedDistanceBetweenFractions(0, min.Fraction, out var dist); maxCurve.SignedDistanceBetweenFractions(0, max.Fraction, out var dist1); //var l = (dist1 - dist) / Session.Instance.GetActiveDgnModel().GetModelInfo().UorPerMeter > 5000; if (minIndex == maxIndex) //是否在同一条线上 { return (dist1 - dist) / Session.Instance.GetActiveDgnModel().GetModelInfo().UorPerMeter; } else if (maxIndex > minIndex) { minCurve.Length(out var minLength); //maxCurve.Length(out var maxlength); var minDis = minLength - dist;//前置曲线的后半段 //var maxDis = max.Fraction * maxlength;//后置曲线的前半段 var length = minDis + dist1; if (maxIndex - minIndex > 1)//不连续,中间还有其他的线 { for (var i = minIndex + 1; i < maxIndex; i++) { curveVector.GetPrimitive((int)i).Length(out var l); length += l; } } return length / Session.Instance.GetActiveDgnModel().GetModelInfo().UorPerMeter; } } return 0.0; } /// /// 判断元素是否是曲线 /// /// 元素 /// 是否是曲线 private static bool IsCurve(Element elem) { return elem is LineElement || elem is ComplexStringElement || elem is LineStringBaseElement || elem is EllipseElement || elem is ComplexShapeElement || elem is BSplineCurveElement; } /// /// 获取ItemType,包含多个CustomProperty /// /// /// /// private static ItemType GetItemType(string itemLibName, string itemTypeName) { DgnFile dgnFile = Session.Instance.GetActiveDgnFile(); //根据声明名称获取对应的ItemTypeLibrary ItemTypeLibrary itemTypeLibrary = ItemTypeLibrary.FindByName(itemLibName, dgnFile); return itemTypeLibrary?.GetItemTypeByName(itemTypeName); } /// /// 挂接ItemType到元素 /// /// /// /// private static void AttachItemType(Element elem, string itemLibName, string itemTypeName) { DgnFile dgnFile = Session.Instance.GetActiveDgnFile();//获得当前激活的文件 DgnModel dgnModel = Session.Instance.GetActiveDgnModel(); //根据声明名称获取对应的ItemType ItemType itemType = GetItemType(itemLibName, itemTypeName); CustomItemHost host = new CustomItemHost(elem, true);//声明元素的CustomItemHost //实例化 IDgnECInstance item = host.ApplyCustomItem(itemType, true);//对CustomItemHost添加ItemType得到EC实例 //EditParameterDefinitions defs = EditParameterDefinitions.GetForModel(dgnModel);//获得模型中的参数定义 //DgnECInstanceEnabler enabler = DgnECManager.Manager.ObtainInstanceEnabler(dgnFile, itemType.ECClass);//获得文件中使用指定EC类的EC实例 //if (null != enabler && enabler.SupportsCreateInstanceOnElement)//判断是否是否支持对元素挂接EC实例 // defs.SetDomainParameters(enabler.SharedWipInstance);//对参数定义设置域参数 item.ScheduleChanges(elem);//对元素更新ItemType信息 elem.ReplaceInModel(elem); } /// /// 元素之间传递ItemTypes及值 /// /// /// private static void DeliverItemTypesTo(Element source, Element target) { CustomItemHost host = new CustomItemHost(source, true);//声明元素的CustomItemHost var instances = host.CustomItems; //List list = new List(); foreach (var instance in instances) { var ecClass = instance.ClassDefinition;//ItemTypes var lib = ecClass.Schema;//ItemTypesLibrary AttachItemType(target, lib.DisplayLabel, ecClass.DisplayLabel); if (instance.ContainsValues) { foreach (var propValue in instance) { if (propValue.TryGetNativeValue(out var value)) { var prop = propValue.Property; SetECPropertyValues(target, prop.DisplayLabel, value); } } } } } /// /// 通过属性名设置属性值,多个同名属性会同时设置 /// /// 元素 /// 属性名 /// private static void SetECPropertyValues(Element elem, string propName, object value) { var instances = new CustomItemHost(elem, false).CustomItems; try { foreach (var instance in instances) { instance.SetValue(propName, value); instance.WriteChanges(); } } catch (Exception) { } } /// /// 获取所有属性 /// /// private static List GetProperyValues(Element element, string propName) { if (null == element) { throw new ArgumentNullException(nameof(element), "所提供ElemId无效"); } List eCProperties = new List(); DgnECManager ecManager = DgnECManager.Manager; DgnECInstanceCollection instCol = ecManager.GetElementProperties(element, ECQueryProcessFlags.SearchAllClasses); foreach (IDgnECInstance inst in instCol) { IEnumerator propertyEnum = inst.ClassDefinition.GetEnumerator(); while (propertyEnum.MoveNext()) { IECPropertyValue propertyValue = inst.GetPropertyValue(propertyEnum.Current.Name); if (propertyValue == null) { continue; } if (propertyValue.IsArray) { IECArrayValue arrayVal = propertyValue as IECArrayValue; if (arrayVal.Count >= 1) propertyValue = arrayVal[0]; } var prop = propertyEnum.Current.Name; ECNameValidation.DecodeFromValidName(ref prop); if (propName == prop) { eCProperties.Add(propertyValue); } } } return eCProperties; } /// /// 获取元素的图层,如果是一般单元,则按第一个子元素来获取 /// /// /// private static LevelHandle GetLevel(Element element) { ElementPropertiesGetter getter = new ElementPropertiesGetter(element); var lvlcache = Session.Instance.GetActiveDgnFile().GetLevelCache(); //一般单元 if (element.GetChildren().Count() > 0) { foreach (var elem in element.GetChildren()) { if (elem is DisplayableElement && !elem.IsInvisible) { return GetLevel(elem); } } } return lvlcache.GetLevel(getter.Level, true); } /// /// 初支按TC-400-结构组成属性获取上部或下部或全部的元素 /// /// /// private static IEnumerable GetElementsFiltered(IEnumerable elements) { var groups = elements.GroupBy(e => GetProperyValues(e, "TC-400-结构组成").FirstOrDefault(p => !string.IsNullOrEmpty(p.StringValue))?.StringValue?.Trim()); foreach (var item in groups) { if (item.Key == "全部" || item.Key == "上部" || item.Key == "下部") { return item.ToList(); } } return new List(); } /// /// 获取每个初支或二衬拱墙图层上的网格元素在中心线上的投影长度 /// /// 中心线图层所在图层 /// 实体属性名,如果存在初支或者拱墙上下部的情况,需要 /// 实体属性值 /// 网格元素和投影长度的集合 /// public static Dictionary GetMeshProjectLength(string centerLineLevel) { Dictionary meshLengthDictionary = new Dictionary(); DgnModel dgnModel = Session.Instance.GetActiveDgnModel(); var levelCache = dgnModel.GetLevelCache(); try { //获取用于计算长度的模型 List models = new List(); //初支元素 var primaryLinings = dgnModel.GetGraphicElements() .Where(e => e.IsValid && !e.IsInvisible && GetLevel(e).DisplayName.Trim().ToUpper() == "JG_XTZH_PH"); //primaryLinings = GetElementsFiltered(primaryLinings); //包含初支Mesh if (primaryLinings.Any()) { models.AddRange(primaryLinings); } else//查找二衬 { //二衬元素仰拱 var secondaryLinings = dgnModel.GetGraphicElements() .Where(e => e.IsValid && !e.IsInvisible && (GetLevel(e)?.DisplayName.Trim().ToUpper() == "JG_CQ_GQ")); if (secondaryLinings.Any()) { models.AddRange(secondaryLinings); } } if (!models.Any()) { MessageCenter.Instance.ShowInfoMessage("不存在可以用于计算长度的元素的mesh元素", "", false); return meshLengthDictionary; } foreach (var model in models) { if (model is MeshHeaderElement)//网格的情况 { //需要拆分则subMeshIds为拆分后的网格ID,否则为原网格ID,即只有一个 DivMesh(model, out List subMeshIds); //遍历网格元素,获取元素到中线的投影长度 foreach (var itemIds in subMeshIds) { long lEleId = itemIds; ElementId eleIdMeshTest = new ElementId(ref lEleId); Element elem = Session.Instance.GetActiveDgnModel().FindElementById(eleIdMeshTest); var subLength = GetMeshLength(elem as MeshHeaderElement, centerLineLevel); meshLengthDictionary.Add(elem, subLength); } } else//不是网格元素,需要转为网格计算 { //可以拆分 //将非网格的元素转成网格。 MyMeshProcessor tool = new MyMeshProcessor(); ElementGraphicsOutput.Process(model, tool); var resultLength = tool.resultList.Sum(elem => GetMeshLength(elem, centerLineLevel)); foreach (var item in tool.resultList) { item.DeleteFromModel(); } meshLengthDictionary.Add(model, resultLength); } } } catch (Exception ex) { MessageBox.Show(ex.Message); } return meshLengthDictionary; } } }