Files
ShrlAlgoToolkit/ShrlAlgoToolkit.Revit/Extensions/MEPExtensions.cs
2025-04-24 20:56:44 +08:00

486 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
using System.Diagnostics;
namespace ShrlAlgoToolkit.Revit.Assists;
/// <summary>
/// MEP扩展类
/// </summary>
/// <remarks>管线连接件CoordinateSystem的BasisX为连接件的-BasisZ叉乘BasisY</remarks>
/// <remarks>管线连接件CoordinateSystem的BasisY为连接件的底部向下</remarks>
/// <remarks>管线连接件CoordinateSystem的BasisZ为连接件的法向朝外</remarks>
public static class MEPExtensions
{
/// <summary>
/// 从连接件延伸长度
/// </summary>
/// <param name="mepCurve"></param>
/// <returns></returns>
public static double GetExtensionLength(this MEPCurve mepCurve)
{
foreach (Connector conn in mepCurve.GetConnectors())
{
return conn.GetExtensionLength();
}
return -1;
}
/// <summary>
/// 两条异面直线的公垂线(两直线距离长度最短)与异面直线的交点
/// </summary>
/// <param name="mepCurve"></param>
/// <param name="mepCurve1"></param>
/// <returns></returns>
public static Dictionary<MEPCurve, XYZ> GetNearestPoints(this MEPCurve mepCurve, MEPCurve mepCurve1)
{
Dictionary<MEPCurve, XYZ> dictionary = [];
var line1 = mepCurve.GetCurve() as Line;
var line2 = mepCurve1.GetCurve() as Line;
var a = line1.Direction.DotProduct(line2.Direction);
var b = line1.Direction.DotProduct(line1.Direction);
var c = line2.Direction.DotProduct(line2.Direction);
var d = (line2.GetEndPoint(0) - line1.GetEndPoint(0)).DotProduct(line1.Direction);
var e = (line2.GetEndPoint(0) - line1.GetEndPoint(0)).DotProduct(line2.Direction);
double t1;
double t2;
if (a == 0) //表明原始两条直线互相垂直
{
t1 = d / b;
t2 = -e / c;
}
else if (Math.Abs((a * a) - (b * c)) < 0.0001f) //表明共线或平行注意理想情况下应该是a * radian - b * c == 0才认为共线或平行但在计算机世界里有精度这个东西存在所以我们近视的认为数值小于某一个值则认为等于0
{
//说明共线或平行
return dictionary;
}
else
{
t1 = ((a * e) - (c * d)) / ((a * a) - (b * c));
t2 = (b / a * t1) - (d / a);
}
var p1 = line1.GetEndPoint(0) + (t1 * line1.Direction);
var p2 = line2.GetEndPoint(0) + (t2 * line2.Direction);
dictionary.Add(mepCurve, p1);
dictionary.Add(mepCurve1, p2);
return dictionary;
}
/// <summary>
/// 复制并修改定位线,并可用来连接该管线,朝上方向的一致性
/// </summary>
/// <param name="mepCurve">用来复制的管线</param>
/// <param name="line">定位线</param>
/// <returns></returns>
public static MEPCurve CopyAndSetLocationCurve(this MEPCurve mepCurve, Line line)
{
var doc = mepCurve.Document;
var elemId = ElementTransformUtils.CopyElement(doc, mepCurve.Id, XYZ.Zero).FirstOrDefault();
var newMepCurve = doc.GetElement(elemId) as MEPCurve;
newMepCurve.SetLocationCurve(line);
doc.Regenerate();
//立管的断面可能错误,需要旋转截面
if (line.Direction.CrossProduct(XYZ.BasisZ).IsZeroLength())
{
var conns = ConnectorAssist.GetNearestConnectors(mepCurve, newMepCurve);
var hand = conns[0].CoordinateSystem.BasisX;
//取反向
var newHand = conns[1].CoordinateSystem.BasisX;
//点积
var cos = hand.DotProduct(newHand);
//夹角
var radian = Math.Acos(cos);
////旋转轴向
//var d = hand.CrossProduct(newHand);
//当两个BasisX相反时则为可正确连接的不需要旋转
if (hand.IsAlmostEqualTo(-newHand))
{
return newMepCurve;
}
//连接件的BasisZ就是轴向
var d = conns[1].CoordinateSystem.BasisZ;
//以立管或倾斜管作为参考轴
var ax = Line.CreateUnbound(conns[1].Origin, d);
//保证夹角为180度故旋转角用Π-夹角
ElementTransformUtils.RotateElement(doc, elemId, ax, Math.PI - radian);
//double radian = default;
//Line axis = null;
//foreach (Connector connector in mepCurve.ConnectorManager.Connectors)
//{
// foreach (Connector connector1 in newMepCurve.ConnectorManager.Connectors)
// {
// //侧面向量
// var dotProduct = connector1.CoordinateSystem.BasisX.DotProduct(connector.CoordinateSystem.BasisX);
// radian = Math.Acos(dotProduct);
// //为0的时候即两个侧面向量重合或相反表明要么正好合适要么需要旋转180度
// var direction =
// Math.Sin(radian) < 0.0001 ? XYZ.BasisZ : connector1.CoordinateSystem.BasisX.CrossProduct(connector.CoordinateSystem.BasisX);
// if (!direction.IsZeroLength())
// {
// axis = Line.CreateUnbound(connector1.Origin, direction);
// }
// //radian = Math.PI - radian;
// }
//}
//ElementTransformUtils.RotateElement(doc, elemId, axis, radian);
}
return newMepCurve;
}
/// <summary>
/// 用点打断管线
/// </summary>
/// <param name="mepCurve">原管线,执行后,保留为打断点到原管线终点</param>
/// <param name="point">打断点</param>
/// <remarks>新的管线为原来管线定位线方向,在打断点到原管线起点部分</remarks>
/// <returns>新的管线</returns>
public static ElementId BreakByPoint(this MEPCurve mepCurve, XYZ point)
{
if (mepCurve is not InsulationLiningBase)
{
try
{
var document = mepCurve.Document;
return mepCurve switch
{
Duct duct => MechanicalUtils.BreakCurve(document, duct.Id, point),
Pipe pipe => PlumbingUtils.BreakCurve(document, pipe.Id, point),
_ => mepCurve.BreakCurve(point)
};
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
return ElementId.InvalidElementId;
}
}
throw new InvalidOperationException($"{nameof(mepCurve)}不是管线或不可打断");
}
/// <summary>
/// 打断直线管线
/// </summary>
/// <param name="electricMepCurve"></param>
/// <param name="point"></param>
/// <remarks>新创建的管线距离起点近,返回Id=-1时不能打断管线</remarks>
/// <returns></returns>
private static ElementId BreakCurve(this MEPCurve electricMepCurve, XYZ point)
{
var document = electricMepCurve.Document;
var application = document.Application;
var conns = electricMepCurve.ConnectorManager.Connectors.OfType<Connector>().Where(c => c.ConnectorType == ConnectorType.End);
//原管线连接的接头
var originConn1 = conns.ElementAt(0);
var originConn2 = conns.ElementAt(1);
//已连接的连接件
var connectedConn1 = originConn1.GetConnectedConnector();
var connectedConn2 = originConn2.GetConnectedConnector();
Debug.WriteLine($"起点连接件{connectedConn1}");
Debug.WriteLine($"终点连接件{connectedConn2}");
connectedConn1?.DisconnectFrom(originConn1);
connectedConn2?.DisconnectFrom(originConn2);
document.Regenerate();
//投影点
if (
electricMepCurve.GetCurve() is not Line line
|| electricMepCurve is FlexDuct
|| electricMepCurve is FlexPipe
|| electricMepCurve is PipeInsulation
|| electricMepCurve is DuctInsulation
)
{
//throw new InvalidOperationException("不可打断的类型");
return ElementId.InvalidElementId;
}
var projectPoint = line.Project(point).XYZPoint;
if ((electricMepCurve.GetCurve() as Line).IsInsideEx(point, application.ShortCurveTolerance))
{
var startLine = Line.CreateBound(line.GetEndPoint(0), projectPoint);
var endLine = Line.CreateBound(projectPoint, line.GetEndPoint(1));
var electricMepCurveCopyId = ElementTransformUtils.CopyElement(document, electricMepCurve.Id, XYZ.Zero).FirstOrDefault();
var electricMepCurveCopy = document.GetElement(electricMepCurveCopyId) as MEPCurve;
//起点线
electricMepCurveCopy.SetLocationCurve(startLine);
electricMepCurve.SetLocationCurve(endLine);
Debug.WriteLine($"设置定位线");
document.Regenerate();
Debug.WriteLine($"原有接头{connectedConn1?.Origin}");
Debug.WriteLine($"原有接头{connectedConn2?.Origin}");
foreach (Connector conn1 in electricMepCurveCopy!.ConnectorManager.Connectors)
{
Debug.WriteLine($"复制管连接件{conn1.Origin}");
if (connectedConn1 != null)
{
if (conn1.Origin.DistanceTo(connectedConn1.Origin) < application.ShortCurveTolerance)
{
Debug.WriteLine("复制管连接连接件1");
conn1.ConnectTo(connectedConn1);
break;
}
}
if (connectedConn2 != null)
{
if (conn1.Origin.DistanceTo(connectedConn2.Origin) < application.ShortCurveTolerance)
{
Debug.WriteLine("复制管连接连接件2");
conn1.ConnectTo(connectedConn2);
break;
}
}
}
foreach (Connector conn1 in electricMepCurve!.ConnectorManager.Connectors)
{
if (connectedConn1 != null)
{
if (conn1.Origin.DistanceTo(connectedConn1.Origin) < application.ShortCurveTolerance)
{
Debug.WriteLine("原有管连接连接件1");
conn1.ConnectTo(connectedConn1);
break;
}
}
if (connectedConn2 != null)
{
if (conn1.Origin.DistanceTo(connectedConn2.Origin) < application.ShortCurveTolerance)
{
Debug.WriteLine("原有管连接连接件2");
conn1.ConnectTo(connectedConn2);
break;
}
}
}
return electricMepCurveCopyId;
}
return ElementId.InvalidElementId;
}
public static FamilySymbol GetFittingFromConduit(
this ConduitType condType,
RoutingPreferenceRuleGroupType groupType,
double pipeDiameter,
Document doc
)
{
FamilySymbol familySymbol = null;
var routingPreferenceManager = condType.RoutingPreferenceManager;
for (var num = 0; num != routingPreferenceManager.GetNumberOfRules(groupType); num++)
{
var rule = routingPreferenceManager.GetRule(groupType, num);
var primarySizeCriterion = rule.GetCriterion(0) as PrimarySizeCriterion;
if (
Math.Round(primarySizeCriterion.MinimumSize * 12.0, 2) <= Math.Round(pipeDiameter, 2)
&& Math.Round(pipeDiameter, 2) <= Math.Round(primarySizeCriterion.MaximumSize * 12.0, 2)
)
{
var meppartId = rule.MEPPartId;
familySymbol = doc.GetElement(meppartId) as FamilySymbol;
if (!familySymbol.Name.Contains("Tap"))
{
break;
}
}
}
return familySymbol;
}
public static FamilySymbol GetFittingFromRouting(this PipeType pipeType, RoutingPreferenceRuleGroupType groupType, double pipeDiameter, Document doc)
{
FamilySymbol familySymbol = null;
var routingPreferenceManager = pipeType.RoutingPreferenceManager;
for (var num = 0; num != routingPreferenceManager.GetNumberOfRules(groupType); num++)
{
var rule = routingPreferenceManager.GetRule(groupType, num);
var primarySizeCriterion = rule.GetCriterion(0) as PrimarySizeCriterion;
if (
Math.Round(primarySizeCriterion.MinimumSize * 12.0, 2) <= Math.Round(pipeDiameter, 2)
&& Math.Round(pipeDiameter, 2) <= Math.Round(primarySizeCriterion.MaximumSize * 12.0, 2)
)
{
var meppartId = rule.MEPPartId;
familySymbol = doc.GetElement(meppartId) as FamilySymbol;
if (!familySymbol.Name.Contains("Tap"))
{
break;
}
}
}
return familySymbol;
}
/// <summary>
/// 添加管道尺寸
/// </summary>
/// <param name="pipe"></param>
/// <param name="nominal">公制</param>
/// <param name="inner"></param>
/// <param name="outer"></param>
public static void AddPipeSize(this Pipe pipe, double nominal, double inner, double outer)
{
var pt = pipe.PipeType;
var rpm = pt.RoutingPreferenceManager;
var rpr = rpm.GetRule(RoutingPreferenceRuleGroupType.Segments, 0); //获取管段设置数量
//var eid = rpr.MEPPartId;
//int num = rpm.GetNumberOfRules(RoutingPreferenceRuleGroupType.Segments);
//PipeSegment ps = doc.GetElement(eid) as PipeSegment;
//存在多个管段,获取第一个
var ps = pipe.PipeSegment;
MEPSize mepSize = new(nominal / 304.8, inner / 304.8, outer / 304.8, true, true);
ps.AddSize(mepSize);
//RoutingPreferenceRule rule = new RoutingPreferenceRule(eid, "");
if (rpr.GetCriterion(0) is PrimarySizeCriterion psc && psc.MaximumSize < nominal / 304.8)
{
psc.MaximumSize = nominal / 304.8;
}
//rpr.AddCriterion(new PrimarySizeCriterion(6 / 304.8, pipesize));
//rpm.AddRule(RoutingPreferenceRuleGroupType.Segments, rpr);
//pipe.get_Parameter(BuiltInParameter.RBS_CURVE_VERT_OFFSET_PARAM).Set(2);//顶部
//pipe.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).Set(pipesize);
}
/// <summary>
/// 主次分支连接
/// </summary>
/// <param name="mainMepCurve">主管</param>
/// <param name="branchMepCurve">分支</param>
public static void ConnectTo(this MEPCurve mainMepCurve, MEPCurve branchMepCurve)
{
var document = mainMepCurve.Document;
var unboundBranchCurve = branchMepCurve.GetCurve();
unboundBranchCurve.MakeUnbound();
var unboundMainCurve = mainMepCurve.GetCurve();
unboundMainCurve.MakeUnbound();
var intersection = unboundBranchCurve.IntersectionPoint(unboundMainCurve) ?? throw new InvalidOperationException("主次分支管线不存在交点");
var line = mainMepCurve.GetCurve() as Line;
//根据交点在管线内的位置,判断是形成弯头还是三通
if (line.IsInsideEx(intersection, 0.5)) //三通
{
var newMainMEPCurveId = mainMepCurve.BreakByPoint(intersection);
var newMainMEPCurve = document.GetElement(newMainMEPCurveId) as MEPCurve;
var mainConnectors = ConnectorAssist.GetNearestConnectors(mainMepCurve, newMainMEPCurve);
var branchConnector = branchMepCurve.GetConnectors(true).GetNearestConnector(intersection);
branchConnector.ConnectByTee(mainConnectors[0], mainConnectors[1]);
}
else //弯头
{
var conn = mainMepCurve.GetConnectors(true).GetNearestConnector(intersection);
var conn2 = branchMepCurve.GetConnectors(true).GetNearestConnector(intersection);
conn.ConnectByFitting(conn2);
}
}
/// <summary>
/// 获取所有连接元素
/// </summary>
/// <param name="mepCurve"></param>
/// <param name="elemIdsConnected">连接的所有元素ID</param>
public static void GetAllConnectedElements(this Element mepCurve, List<ElementId> elemIdsConnected)
{
var allConnectors = mepCurve.GetConnectors();
foreach (Connector connector in allConnectors)
{
if (!connector.IsConnected)
{
continue;
}
if (!elemIdsConnected.Contains(connector.Owner.Id))
{
elemIdsConnected.Add(connector.Owner.Id);
}
//连接的元素
//得到与当前连接件连接的连接件
foreach (Connector currentConn in connector.AllRefs)
{
//连接的连接件
if (currentConn.ConnectorType == ConnectorType.End && currentConn.Owner.Id != connector.Owner.Id)
{
if (elemIdsConnected.Contains(currentConn.Owner.Id))
{
continue;
}
elemIdsConnected.Add(currentConn.Owner.Id);
GetAllConnectedElements(currentConn.Owner, elemIdsConnected);
//var conns = currentConn.Owner.GetConnectors();
//foreach (Connector item in conns)
//{
// if (startConnector.Origin.IsAlmostEqualTo(item.Origin))
// {
// continue;
// }
// GetAllRefsElements(item, elemIdsConnected);
//}
}
}
}
}
/// <summary>
/// 管线与墙的相交(射线法)
/// </summary>
/// <param name="pipe"></param>
public static List<T> GetPipeIntersector<T>(this Pipe pipe)
{
var doc = pipe.Document;
var v = doc.ActiveView as View3D;
List<T> li = [];
//线段长度
var pline = pipe.GetCurve() as Line;
var p1 = pline.GetEndPoint(0);
var p2 = pline.GetEndPoint(1);
//线段方向
var dir = (p2 - p1).Normalize();
var length = pline.Length;
ElementClassFilter filter = new(typeof(T));
ReferenceIntersector rinter = new(filter, FindReferenceTarget.Element, v);
var rwc = rinter.Find(p1, dir);
foreach (var r in rwc)
{
if (r.Proximity < length)
{
var elem = doc.GetElement(r.GetReference());
if (elem is T t)
{
li.Add(t);
}
}
}
return li;
//MessageBox.Show("墙的数量为:" + i, "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
}