Files
ShrlAlgoToolkit/ShrlAlgo.RvKits/RvMEP/ArrangeMEPCurveViewModel.cs

366 lines
24 KiB
C#

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;
namespace ShrlAlgo.RvKits.RvMEP
{
public partial class ArrangeMEPCurveViewModel : ObservableObject
{
private readonly ActionEventHandler arrangeHandler = new();
[ObservableProperty]
private double gap = 250;
[ObservableProperty]
private bool mEPCurveCenter = true;
[ObservableProperty]
private bool isHorizon = true;
[RelayCommand]
private void Arrange()
{
arrangeHandler.Raise(
uiapp =>
{
var uidoc = uiapp.ActiveUIDocument;
var doc = uidoc.Document;
doc.Invoke(
ts =>
{
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<ArrangeElement> 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 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<Connector>()
.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<Connector>()
.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; }
/// <summary>
/// 与基准元素的间距及方向向量
/// </summary>
public XYZ HorizonDistanceVector { get; set; }
}
}