using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
namespace Sai.Toolkit.Revit.Assist;
///
/// MEP扩展类
///
/// 管线连接件CoordinateSystem的BasisX为连接件的(-BasisZ叉乘BasisY)
/// 管线连接件CoordinateSystem的BasisY为连接件的底部向下
/// 管线连接件CoordinateSystem的BasisZ为连接件的法向朝外
public static class MEPAssist
{
///
/// 从连接件延伸长度
///
///
///
public static double GetExtensionLength(this MEPCurve mepCurve)
{
foreach (Connector conn in mepCurve.GetConnectors())
{
return conn.GetExtensionLength();
}
return -1;
}
///
/// 两条异面直线的公垂线(两直线距离长度最短)与异面直线的交点
///
///
///
///
public static Dictionary GetNearestPoints(this MEPCurve mepCurve, MEPCurve mepCurve1)
{
Dictionary dictionary = [];
var line1 = mepCurve.GetLocCurve() as Line;
var line2 = mepCurve1.GetLocCurve() 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;
}
///
/// 复制并修改定位线,并可用来连接该管线,朝上方向的一致性
///
/// 用来复制的管线
/// 定位线
/// 可以创建弯头
///
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;
}
///
/// 用点打断管线
///
/// 原管线,执行后,保留为打断点到原管线终点
/// 打断点
/// 新的管线为原来管线定位线方向,在打断点到原管线起点部分
/// 新的管线
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)}不是管线或不可打断");
}
///
/// 打断直线管线
///
///
///
/// 新创建的管线距离起点近,返回Id=-1时,不能打断管线
///
private static ElementId BreakCurve(this MEPCurve electricMepCurve, XYZ point)
{
var document = electricMepCurve.Document;
var application = document.Application;
var conns = electricMepCurve.ConnectorManager.Connectors.OfType().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.GetLocCurve() 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.GetLocCurve() 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;
}
///
/// 添加管道尺寸
///
///
/// 公制
///
///
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);
}
///
/// 主次分支连接
///
/// 主管
/// 分支
public static void ConnectTo(this MEPCurve mainMepCurve, MEPCurve branchMepCurve)
{
var document = mainMepCurve.Document;
var unboundBranchCurve = branchMepCurve.GetLocCurve();
unboundBranchCurve.MakeUnbound();
var unboundMainCurve = mainMepCurve.GetLocCurve();
unboundMainCurve.MakeUnbound();
var intersection = unboundBranchCurve.IntersectionPoint(unboundMainCurve) ?? throw new InvalidOperationException("主次分支管线不存在交点");
var line = mainMepCurve.GetLocCurve() 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);
}
}
///
/// 获取所有连接元素
///
///
/// 连接的所有元素ID
public static void GetAllConnectedElements(this Element mepCurve, List 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);
//}
}
}
}
}
///
/// 管线与墙的相交(射线法)
///
///
public static List GetPipeIntersector(this Pipe pipe)
{
var doc = pipe.Document;
var v = doc.ActiveView as View3D;
List li = new();
//线段长度
var pline = pipe.GetLocCurve() 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);
}
}