using System.Windows; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Electrical; using Autodesk.Revit.DB.Mechanical; using Autodesk.Revit.DB.Plumbing; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Nice3point.Revit.Toolkit.External.Handlers; using ShrlAlgoToolkit.RevitAddins.RvMEP; using ShrlAlgoToolkit; using ShrlAlgoToolkit.RevitAddins; namespace ShrlAlgoToolkit.RevitAddins.Mep { public partial class ArrangeMEPCurveViewModel : ObservableObject { private readonly ActionEventHandler arrangeHandler = new(); [ObservableProperty] public partial double Gap { get; set; } = 250; [ObservableProperty] public partial bool MEPCurveCenter { get; set; } = true; [ObservableProperty] public partial bool IsHorizon { get; set; } = true; [RelayCommand] private void Arrange() { arrangeHandler.Raise( uiapp => { var uidoc = uiapp.ActiveUIDocument; var doc = uidoc.Document; doc.Invoke( _ => { try { var ids = uidoc.Selection .PickElementsByRectangle( new FuncFilter( e => e is MEPCurve && e is not InsulationLiningBase && e is not FlexDuct && e is not FlexPipe && !(e.GetCurve() as Line).Direction.IsParallelTo(XYZ.BasisZ)), "请选择要整理的管线") .Select(e => e.Id) .ToList(); if (ids.Count <= 1) { return; } var baseId = uidoc.Selection .PickObject( Autodesk.Revit.UI.Selection.ObjectType.Element, new FuncFilter(e => ids.Contains(e.Id)), "请选择基准管线") .ElementId; var baseElement = doc.GetElement(baseId); var baseLine = baseElement.GetCurve() as Line; baseLine.MakeUnbound(); if (IsHorizon) { //移除基准 ids.Remove(baseId); List arranges = []; foreach (var id in ids) { var elem = doc.GetElement(id); var l = elem.GetCurve() as Line; if (!l.Direction.IsParallelTo(baseLine.Direction)) { MessageBox.Show("所选管线不平行", "错误"); return; } var d = baseLine.Distance(l.Origin); var project = baseLine.Project(l.Origin).XYZPoint; var vector = l.Origin.Flatten() - project.Flatten(); var ae = new Mep.ArrangeElement { ElementToMove = elem, //用来确定与基准管线的位置和方向 HorizonDistanceVector = vector }; arranges.Add(ae); } var groups = arranges.OrderBy(ar => ar.HorizonDistanceVector.GetLength()) .GroupBy( ar => XYZ.BasisZ .CrossProduct(baseLine.Direction) .IsAlmostEqualTo(ar.HorizonDistanceVector.Normalize())); if (groups.Count() > 2) { MessageBox.Show("管线定位线存在误差"); return; } //管中心距离 if (MEPCurveCenter) { foreach (var group in groups) { for (var i = 0; i < group.Count(); i++) { var arrange = group.ElementAt(i); //当前的距离 var dis = arrange.HorizonDistanceVector.GetLength(); var moveUnitDir = arrange.HorizonDistanceVector.Normalize(); //最终的距离 var actualDis = (i + 1) * Gap / 304.8; var translate = moveUnitDir * (actualDis - dis); ElementTransformUtils.MoveElement( doc, arrange.ElementToMove.Id, translate); } } } else { //遍历两个方向的分组 foreach (var group in groups) { double actualDis = 0; double previousHalfSize = 0; //基准半宽 if (baseElement is Pipe pipe) { previousHalfSize = pipe.get_Parameter(BuiltInParameter.RBS_PIPE_OUTER_DIAMETER).AsDouble() / 2; } else if (baseElement is Conduit conduit) { previousHalfSize = conduit.get_Parameter(BuiltInParameter.RBS_CONDUIT_OUTER_DIAM_PARAM).AsDouble() / 2; } else if (baseElement is CableTray tray || baseElement is Duct duct) { var baseConnector = baseElement.GetConnectors() .OfType() .FirstOrDefault(); if (baseConnector.Shape == ConnectorProfileType.Oval || baseConnector.Shape == ConnectorProfileType.Rectangular) { previousHalfSize = baseConnector.Width / 2; } else if (baseConnector.Shape == ConnectorProfileType.Round) { previousHalfSize = baseConnector.Radius; } } if (baseElement is Pipe or Duct) { //基准保温层厚度 var insulationId = InsulationLiningBase.GetInsulationIds( doc, baseElement.Id).FirstOrDefault(); if (insulationId != null) { var insulation = doc.GetElement(insulationId) as InsulationLiningBase; previousHalfSize += insulation.Thickness; } } for (var i = 0; i < group.Count(); i++) { var arrange = group.ElementAt(i); //当前的距离 var dis = arrange.HorizonDistanceVector.GetLength(); var moveUnitDir = arrange.HorizonDistanceVector.Normalize(); double currentHalfSize = 0; if (arrange.ElementToMove is Pipe p) { currentHalfSize = p.get_Parameter(BuiltInParameter.RBS_PIPE_OUTER_DIAMETER).AsDouble() / 2; } else if (arrange.ElementToMove is Conduit conduit) { currentHalfSize = conduit.get_Parameter(BuiltInParameter.RBS_CONDUIT_OUTER_DIAM_PARAM).AsDouble() / 2; } else if (arrange.ElementToMove is CableTray tray || arrange.ElementToMove is Duct duct) { var conn = arrange.ElementToMove.GetConnectors() .OfType() .FirstOrDefault(); if (conn.Shape == ConnectorProfileType.Oval || conn.Shape == ConnectorProfileType.Rectangular) { currentHalfSize = conn.Width / 2; } else if (conn.Shape == ConnectorProfileType.Round) { currentHalfSize = conn.Radius; } } //只有管道和风管有保温 if (arrange.ElementToMove is Pipe or Duct) { //考虑保温层 var currentInsulationId = InsulationLiningBase.GetInsulationIds( doc, arrange.ElementToMove.Id).FirstOrDefault(); if (currentInsulationId != null) { var currentInsulation = doc.GetElement(currentInsulationId) as InsulationLiningBase; currentHalfSize += currentInsulation.Thickness; } } //最终的距离叠加,附加上次的距离,使得每次只要增加一个上一个半宽+当前的半宽+间隙 actualDis += previousHalfSize + currentHalfSize + Gap / 304.8; var translate = moveUnitDir * (actualDis - dis); ElementTransformUtils.MoveElement( doc, arrange.ElementToMove.Id, translate); //修改上个半宽,用于当前分组里下一个元素循环 previousHalfSize = currentHalfSize; } } } } //垂向 else { //通过LocationCurve确定相对位置关系 var orderIds = ids.Select(id => doc.GetElement(id) as MEPCurve) .OrderBy(c => (c.GetCurve() as Line).Origin.Z).Select(e => e.Id).ToList(); //中心距 if (MEPCurveCenter) { var baseIndex = orderIds.IndexOf(baseId); for (var i = 0; i < orderIds.Count; i++) { var currentZ = (doc.GetElement(orderIds[i]).GetCurve() as Line).Origin.Z; var dis = Math.Abs(baseLine.Origin.Z - currentZ); var actualDis = Math.Abs((baseIndex - i)) * Gap / 304.8; //判断方向 var vector = i > baseIndex ? XYZ.BasisZ : -XYZ.BasisZ; var translate = (actualDis - dis) * vector; ElementTransformUtils.MoveElement(doc, orderIds[i], translate); //if (i > baseIndex) //{ // var dis = currentZ - baseLine.Origin.Z;//正的 // var actualDis = (i - baseIndex) * Gap / 304.8;//正的 // var translate = (actualDis - dis) * XYZ.BasisZ; // ElementTransformUtils.MoveElement(doc, orderIds[i], translate); //} } } //外距 else { ids.Remove(baseId); var groups = ids.Select(id => doc.GetElement(id) as MEPCurve) .OrderBy(c => (c.GetCurve() as Line).Origin.Z).GroupBy(e => (e.GetCurve() as Line).Origin.Z > baseLine.Origin.Z); var baseTop = baseElement.get_BoundingBox(null).Max.Z; var baseBottom = baseElement.get_BoundingBox(null).Min.Z; if (baseElement is Pipe or Duct) { //基准保温层厚度 var insulationId = InsulationLiningBase.GetInsulationIds( doc, baseElement.Id).FirstOrDefault(); if (insulationId != null) { var insulation = doc.GetElement(insulationId) as InsulationLiningBase; baseTop = insulation.get_BoundingBox(null).Max.Z; baseBottom = insulation.get_BoundingBox(null).Min.Z; } } foreach (var group in groups) { if (group.Key) { var sumHeight = 0.0; var vector = XYZ.BasisZ; for (var i = 0; i < group.Count(); i++) { var mep = group.ElementAt(i); var currentTop = mep.get_BoundingBox(null).Max.Z; var currentBottom = mep.get_BoundingBox(null).Min.Z; if (mep is Pipe or Duct) { //基准保温层厚度 var insulationId = InsulationLiningBase.GetInsulationIds( doc, mep.Id).FirstOrDefault(); if (insulationId != null) { var insulation = doc.GetElement(insulationId) as InsulationLiningBase; currentTop = insulation.get_BoundingBox(null).Max.Z; currentBottom = insulation.get_BoundingBox(null).Min.Z; } } //当前间距 var dis = currentBottom - baseTop; var actualDis = (i + 1) * Gap / 304.8 + sumHeight; var translate = (actualDis - dis) * vector; ElementTransformUtils.MoveElement(doc, mep.Id, translate); //整体高度 sumHeight += currentTop - currentBottom; } } else { var sumHeight = 0.0; var vector = -XYZ.BasisZ; for (var i = group.Count() - 1; i >= 0; i--) { var mep = group.ElementAt(i); var currentTop = mep.get_BoundingBox(null).Max.Z; var currentBottom = mep.get_BoundingBox(null).Min.Z; if (mep is Pipe or Duct) { //基准保温层厚度 var insulationId = InsulationLiningBase.GetInsulationIds( doc, mep.Id).FirstOrDefault(); if (insulationId != null) { var insulation = doc.GetElement(insulationId) as InsulationLiningBase; currentTop = insulation.get_BoundingBox(null).Max.Z; currentBottom = insulation.get_BoundingBox(null).Min.Z; } } //当前间距 var dis = baseBottom - currentTop; var actualDis = (group.Count() - i) * Gap / 304.8 + sumHeight; var translate = (actualDis - dis) * vector; ElementTransformUtils.MoveElement(doc, mep.Id, translate); //整体高度 sumHeight += currentTop - currentBottom; } } } } } } catch (Autodesk.Revit.Exceptions.OperationCanceledException) { } }, "整理管线"); }); } } public class ArrangeElement { public double ElementHeight => ElementToMove.get_BoundingBox(null).Max.Z - ElementToMove.get_BoundingBox(null).Min.Z; public Element ElementToMove { get; set; } public BoundingBoxXYZ Box { get; set; } /// /// 与基准元素的间距及方向向量 /// public XYZ HorizonDistanceVector { get; set; } } }