添加项目文件。

This commit is contained in:
GG Z
2026-02-28 21:01:57 +08:00
parent 9fe4e5a9aa
commit 7a229067cc
175 changed files with 18060 additions and 0 deletions

View File

@@ -0,0 +1,598 @@
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
{
/// <summary>
/// 凸包算法
/// </summary>
internal class ConvexHull
{
public static HashSet<DPoint3d> Compute(HashSet<DPoint3d> DPoint3ds)
{
if (DPoint3ds.Count <= 1)
return new HashSet<DPoint3d>(DPoint3ds);
// 按x升序排列x相同则按y升序
var sortedDPoint3ds = DPoint3ds.OrderBy(p => p.X).ThenBy(p => p.Y).ToList();
List<DPoint3d> lowerHull = new List<DPoint3d>();
// 构造下凸包
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<DPoint3d> upperHull = new List<DPoint3d>();
// 构造上凸包,逆序处理
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<DPoint3d>(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);
}
}
/// <summary>
/// 隧道网格投影到中线的帮助类
/// </summary>
public class TunnelLengthHelper
{
/// <summary>
/// 分割具有间隙的网格,引用算量源码的方法
/// </summary>
/// <param name="eleMeshDiv"></param>
/// <param name="listEleId">可拆分后的网格,或不可拆分的原网格</param>
/// <returns></returns>
private static bool DivMesh(Element eleMeshDiv, out List<int> listEleId)
{
listEleId = new List<int>();
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;
}
/// <summary>
/// 获取网格投影到XOY平面的边界点
/// </summary>
/// <param name="eleMesh"></param>
/// <returns></returns>
private static HashSet<DPoint3d> GetBoundariesFromMeshByProject(Element eleMesh)
{
var listPtNew = new HashSet<DPoint3d>();
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<DPoint3d>();
if (itemCur.TryGetLineString(pts) && pts.Count > 2)
{
foreach (var item in pts)
{
listPtNew.Add(item);
}
//listPtNew = new HashSet<DPoint3d>(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;
}
}
/// <summary>
/// 通过点分布获取中心线
/// </summary>
/// <param name="listLineMilGeoData">线元素</param>
/// <param name="listPtAll">拍平后的所有平面点</param>
/// <returns></returns>
private static Element GetCenterLine(List<Element> listLineMilGeoData, HashSet<DPoint3d> 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;
}
/// <summary>
/// 获取网格投影在最近的中线投影长度
/// </summary>
/// <param name="meshHeaderEle">mesh实体</param>
/// <param name="centerLineLevel">中心线所在图层如DG-ZZX-ZX</param>
/// <returns></returns>
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<DPoint3d> 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;
}
/// <summary>
/// 判断元素是否是曲线
/// </summary>
/// <param name="elem">元素</param>
/// <returns>是否是曲线</returns>
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;
}
/// <summary>
/// 获取ItemType包含多个CustomProperty
/// </summary>
/// <param name="itemLibName"></param>
/// <param name="itemTypeName"></param>
/// <returns></returns>
private static ItemType GetItemType(string itemLibName, string itemTypeName)
{
DgnFile dgnFile = Session.Instance.GetActiveDgnFile();
//根据声明名称获取对应的ItemTypeLibrary
ItemTypeLibrary itemTypeLibrary = ItemTypeLibrary.FindByName(itemLibName, dgnFile);
return itemTypeLibrary?.GetItemTypeByName(itemTypeName);
}
/// <summary>
/// 挂接ItemType到元素
/// </summary>
/// <param name="elem"></param>
/// <param name="itemLibName"></param>
/// <param name="itemTypeName"></param>
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);
}
/// <summary>
/// 元素之间传递ItemTypes及值
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
private static void DeliverItemTypesTo(Element source, Element target)
{
CustomItemHost host = new CustomItemHost(source, true);//声明元素的CustomItemHost
var instances = host.CustomItems;
//List<ItemType> list = new List<ItemType>();
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);
}
}
}
}
}
/// <summary>
/// 通过属性名设置属性值,多个同名属性会同时设置
/// </summary>
/// <param name="elem">元素</param>
/// <param name="propName">属性名</param>
/// <returns></returns>
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)
{
}
}
/// <summary>
/// 获取所有属性
/// </summary>
/// <param name="element"></param>
private static List<IECPropertyValue> GetProperyValues(Element element, string propName)
{
if (null == element)
{
throw new ArgumentNullException(nameof(element), "所提供ElemId无效");
}
List<IECPropertyValue> eCProperties = new List<IECPropertyValue>();
DgnECManager ecManager = DgnECManager.Manager;
DgnECInstanceCollection instCol = ecManager.GetElementProperties(element, ECQueryProcessFlags.SearchAllClasses);
foreach (IDgnECInstance inst in instCol)
{
IEnumerator<IECProperty> 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;
}
/// <summary>
/// 获取元素的图层,如果是一般单元,则按第一个子元素来获取
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 初支按TC-400-结构组成属性获取上部或下部或全部的元素
/// </summary>
/// <param name="elements"></param>
/// <returns></returns>
private static IEnumerable<Element> GetElementsFiltered(IEnumerable<Element> 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<Element>();
}
/// <summary>
/// 获取每个初支或二衬拱墙图层上的网格元素在中心线上的投影长度
/// </summary>
/// <param name="centerLineLevel">中心线图层所在图层</param>
/// <param name="meshPropertyName">实体属性名,如果存在初支或者拱墙上下部的情况,需要</param>
/// <param name="meshPropertyValue">实体属性值</param>
/// <returns>网格元素和投影长度的集合</returns>
/// <remarks></remarks>
public static Dictionary<Element, double> GetMeshProjectLength(string centerLineLevel)
{
Dictionary<Element, double> meshLengthDictionary = new Dictionary<Element, double>();
DgnModel dgnModel = Session.Instance.GetActiveDgnModel();
var levelCache = dgnModel.GetLevelCache();
try
{
//获取用于计算长度的模型
List<Element> models = new List<Element>();
//初支元素
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<int> 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;
}
}
}