364 lines
24 KiB
C#
364 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 Sai.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; }
|
|
|
|
}
|
|
} |