486 lines
19 KiB
C#
486 lines
19 KiB
C#
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);
|
||
}
|
||
}
|