using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Structure;
using FWindSoft.Revit.Utils;
using FWindSoft.SystemExtensions;
using FWindSoft.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
namespace FWindSoft.Revit.Mep
{
///
/// 线管管件创建
///
public class ConduitFittingCreator
{
/*
* 预留扩展:使用客户端传入的自定族;
* 可能处理与管件类型相关操作,故各个管线分开处理
*/
///
/// FamilyInstance自动和给定的Connectors进行相连接
///
///
///
///
private static void AutoMatchJoin(FamilyInstance instance, List connectors, JoinContext context)
{
//此数据源不进行ToList处理,为了后边连接状态发生改变时,遍历对象能自动跟着改变
var fiConnectors = instance.GetAllConnectors().Where(c => c.IsPhysical());
//次等匹配,精确匹配连接没有找到完成的,再进行次等连接
List secondConnectors = new List();
#region 精确匹配连接,并构件次等匹配数据级
foreach (Connector connector in connectors)
{
if (!connector.IsPhysical() || connector.IsConnected)
{
continue;
}
var refConnector = ConnectorUtils.GetConnectorByDirection(fiConnectors.ToList(), -connector.CoordinateSystem.BasisZ);
if (refConnector == null)
{
secondConnectors.Add(connector);
continue;
}
if (!refConnector.IsConnected)
{
refConnector.Radius = connector.Radius;
try
{
if (connector.Owner is Conduit conduit)
{
//细微处理,有的不进行此步骤,管道长度会自己调整。有的情况则需要自己调整管道,不可预知,所以暂时统一处理
conduit.ReplaceLocation(connector.Origin, refConnector.Origin);
}
refConnector.ConnectTo(connector);
}
catch (Exception e)
{//为了调试时,查看具体异常。保留e变量的引用
//return false;
}
}
}
#endregion
#region 次等匹配连接
foreach (Connector connector in secondConnectors)
{
//在没有连接的Connector里面去选
var minAngleConnector = ConnectorUtils.GetConnectorByMinAngle(fiConnectors.Where(c => !c.IsConnected).ToList(), -connector.CoordinateSystem.BasisZ, Math.Cos(Math.PI / 4));
if (minAngleConnector != null)
{
ArrangeConnectorJoin(minAngleConnector, connector, context);
}
}
#endregion
}
///
/// fi以外的Connector连接自身Connector
///
///
///
///
private static bool ConnectorJoin(FamilyInstance fi, Connector otherConnector, JoinContext context)
{
var refConnector = fi.GetConnectorByDirection(-otherConnector.CoordinateSystem.BasisZ);
if (refConnector != null)
{
refConnector.Radius = otherConnector.Radius;
try
{
if (otherConnector.Owner is Conduit conduit)
{
//细微处理,有的不进行此步骤,管道长度会自己调整。有的情况则需要自己调整管道,不可预知,所以暂时统一处理
conduit.ReplaceLocation(otherConnector.Origin, refConnector.Origin);
}
refConnector.ConnectTo(otherConnector);
}
catch (Exception e)
{//为了调试时,查看具体异常。保留e变量的引用
return false;
}
return true;
}
else
{
var minAngleConnector = fi.GetConnectorByMinAngle(-otherConnector.CoordinateSystem.BasisZ);
if (minAngleConnector != null)
{
return ArrangeConnectorJoin(minAngleConnector, otherConnector, context);
}
}
return false;
}
///
/// 整理连接(处理Connector朝向未平行的情况)
///
/// 设备的连接点
/// 管道的连接点
///
///
private static bool ArrangeConnectorJoin(Connector fiConnector, Connector conduitConnector, JoinContext context)
{
try
{
if (fiConnector == null || conduitConnector == null)
{
return false;
}
var length = JoinConduitUtils.AttachLength;
var useMepCurve = conduitConnector.Owner as MEPCurve;
var start = new XYZ(fiConnector.Origin.X, fiConnector.Origin.Y, fiConnector.Origin.Z);
var end = start + fiConnector.CoordinateSystem.BasisZ * length;
var newMepCurve = useMepCurve.Copy(start, end);
useMepCurve.ReplaceLocation(conduitConnector.Origin, end);
fiConnector.ConnectTo(newMepCurve.GetConnectorByOrigin(start));
var newEndConnector = newMepCurve.GetConnectorByOrigin(end);
//这个最好要换成自己封装的两个Connector连接的方法
//newEndConnector.ConnectTo(conduitConnector);
NewElbowFitting(newEndConnector, conduitConnector, context);
}
catch (Exception e)
{
return false;
}
return true;
}
#region 封装处理 指定连接构件 连接的两种思路
/*
* 1、根据扩展数据中存储的类型id,调用创建构件的不同重载方法
* 2、根据扩展数据中存储的类型id,动态更改线管连接的连接规则,当然此规则设置成为优先及最高,包含范围
* 全部。这样程序在直接使用NewTeeFitting等api。执行完成之后,再将规则删除
*/
#endregion
#region 活接头连接封装
///
/// 活接头连接
///
///
///
///
///
public static FamilyInstance NewUnionFitting(Connector connector1, Connector connector2, JoinContext context)
{
if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected))
{
//已经连接部分
return null;
}
FamilyInstance fi = null;
if (connector1.CoordinateSystem.BasisZ.IsSameDirection(-connector2.CoordinateSystem.BasisZ))
{
fi = connector1.Owner.Document.Create.NewUnionFitting(connector1, connector2);
}
return fi;
}
#endregion
#region 变径连接封装
///
/// 变径连接
///
///
///
///
///
public static FamilyInstance NewTransitionFitting(Connector connector1, Connector connector2, JoinContext context)
{
if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected))
{
//已经连接部分
return null;
}
FamilyInstance fi = null;
if (connector1.CoordinateSystem.BasisZ.IsSameDirection(-connector2.CoordinateSystem.BasisZ))
{
fi = connector1.Owner.Document.Create.NewTransitionFitting(connector1, connector2);
}
return fi;
}
#endregion
///
/// 弯通连接
///
///
///
///
///
public static FamilyInstance NewElbowFitting(Connector connector1, Connector connector2, JoinContext context)
{
if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected))
{
//已经连接部分
return null;
}
FamilyInstance fi = null;
var doc = connector1.Owner.Document;
var dotValue = connector1.CoordinateSystem.BasisZ.DotProduct(connector2.CoordinateSystem.BasisZ);
if (dotValue.IsZero())
{
//垂直
//var refDirection = connector1.CoordinateSystem.BasisZ.IsParallel(XYZ.BasisZ) ? connector2.CoordinateSystem.BasisZ : connector1.CoordinateSystem.BasisZ;
FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-弯头(五孔)");
var line1 = Line.CreateUnbound(connector1.Origin, connector1.CoordinateSystem.BasisZ);
var line2 = Line.CreateUnbound(connector2.Origin, connector2.CoordinateSystem.BasisZ);
var location = line1.GetSpatialIntersection(line2);
var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural);
FittingWrapper fitting = new FittingWrapper(box);
fitting.Rotate(location, -connector1.CoordinateSystem.BasisZ, -connector2.CoordinateSystem.BasisZ);
JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, new List() { -connector1.CoordinateSystem.BasisZ, -connector2.CoordinateSystem.BasisZ });
ConnectorJoin(box, connector1, context);
ConnectorJoin(box, connector2, context);
fi = box;
}
else if (dotValue.Less(0))
{
//钝角连接
//使用可以调整角度的连接件,需要调整角度.
#region 利用系统创建弯头可以自行修改弯头角度的优势,创建钝角弯头
var useType = connector1.Owner.GetElementType();
if (useType == null)
{
useType = connector2.Owner.GetElementType();
}
if (useType != null)
{
var oldFitting = FittingUtils.GetFitting(useType, FittingType.Elbows);
try
{
FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管弯头 - 平端口 - PVC");
FittingUtils.AddFitting(useType, FittingType.Elbows, fs?.Id);
fi = doc.Create.NewElbowFitting(connector1, connector2);
}
finally
{
FittingUtils.AddFitting(useType, FittingType.Elbows, oldFitting);
}
}
#endregion
}
else
{
//锐角连接.
XYZ baseDirection = -connector1.CoordinateSystem.BasisZ;
XYZ refDirection = -connector2.CoordinateSystem.BasisZ;
var axis = baseDirection.CrossProduct(refDirection);
var result = baseDirection.RotateVector(axis, Math.PI / 2);
var line1 = Line.CreateUnbound(connector1.Origin, connector1.CoordinateSystem.BasisZ);
var line2 = Line.CreateUnbound(connector2.Origin, connector2.CoordinateSystem.BasisZ);
var location = line1.GetSpatialIntersection(line2);
FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-弯头(五孔)");
//var fiDirection= baseDirection.IsParallel(XYZ.BasisZ) ? axis : baseDirection;
var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural);
FittingWrapper fitting = new FittingWrapper(box);
fitting.Rotate(location, baseDirection, result);
JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, new List() { baseDirection, result });
ConnectorJoin(box, connector1, context);
var newConnector = box.GetConnectorByDirection(result);
ArrangeConnectorJoin(newConnector, connector2, context);
fi = box;
}
return fi;
}
#region 三通连接创建
///
/// 三通连接
///
/// connector1需要与connector3垂直
///
///
///
///
public static FamilyInstance NewTeeFitting(Connector connector1, Connector connector2, Connector connector3, JoinContext context)
{
if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected || connector3.IsConnected))
{
//已经连接部分
return null;
}
List newConnectors = ConnectorPatternUtils.ArrangeTeeConnectors(connector1, connector2, connector3);
if(newConnectors==null|| newConnectors.Count!=3)
{
return null;
}
var doc = connector1.Owner.Document;
var line1 = Line.CreateUnbound(newConnectors[0].Origin, newConnectors[0].CoordinateSystem.BasisZ);
var line3 = Line.CreateUnbound(newConnectors[2].Origin, newConnectors[2].CoordinateSystem.BasisZ);
var location = line1.GetSpatialIntersection(line3);
//放置三通,手动连接节点
FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-三通(五孔)");
var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural);
#region 初步定位实现
/*
* 默认是1,3是垂直的,判定第第二个是否满足右手坐标系,不然调转3的方向传入旋转
*/
XYZ direction1 = -newConnectors[0].CoordinateSystem.BasisZ;
XYZ direction2 = -newConnectors[1].CoordinateSystem.BasisZ;
XYZ direction3 = -newConnectors[2].CoordinateSystem.BasisZ;
FittingWrapper fitting = new FittingWrapper(box);
var baseJudge = direction1.CrossProduct(direction3);
var dotValue = baseJudge.DotProduct(direction2);//小于0时,影响右手坐标系形成,进行调整;1和2头可能平行,所以调整1
XYZ matchFirst = dotValue.MoreEqual(0) ? direction1 : -direction1;//由于默认连接件定点朝上,使用时,反向使用
fitting.Rotate(location, matchFirst, direction3);
#endregion
JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, new List() { direction1, direction3, direction2 });
AutoMatchJoin(box, newConnectors, context);
return box;
}
#endregion
///
/// 四通连接
///
/// connector1需要与Connector3垂直
///
///
///
///
///
public static FamilyInstance NewCrossFitting(Connector connector1, Connector connector2, Connector connector3, Connector connector4, JoinContext context)
{
if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected || connector3.IsConnected || connector4.IsConnected))
{
//已经连接部分
return null;
}
Document doc = connector1.Owner.Document;
List inputConnectors = new List() { connector1, connector2, connector3, connector4 };
Connector baseConnector = connector1;
Connector useConnector = null;
for (int i = 1; i < inputConnectors.Count; i++)
{
if (!baseConnector.CoordinateSystem.BasisZ.IsParallel(inputConnectors[i].CoordinateSystem.BasisZ))
{
useConnector = inputConnectors[i];
break;
}
}
var line1 = Line.CreateUnbound(baseConnector.Origin, baseConnector.CoordinateSystem.BasisZ);
var line3 = Line.CreateUnbound(useConnector.Origin, useConnector.CoordinateSystem.BasisZ);
var location = line1.GetSpatialIntersection(line3);
FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-四通");
var box = doc.Create.NewFamilyInstance(location, fs, connector1.Owner.GetLevel(), StructuralType.NonStructural);
#region 根据样式,调整旋转
FittingWrapper fitting = new FittingWrapper(box);
var firstDirection = -line1.Direction;
var secondDirection = -line3.Direction;
var otherConnectors = inputConnectors.Where(c => c != baseConnector && c != useConnector).ToList();
var crossDirection = firstDirection.CrossProduct(secondDirection);
var comValue = -Math.Sqrt(2) / 2;//比较值,夹角cos值小于该值得,变换旋转方向
if (otherConnectors.Any(c => (-c.CoordinateSystem.BasisZ).DotProduct(crossDirection).LessEqual(comValue)))
{
firstDirection = -firstDirection;
}
fitting.Rotate(location, firstDirection, secondDirection);
#endregion
var useMatchDirection = inputConnectors.Select(c => -c.CoordinateSystem.BasisZ).ToList();
JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, useMatchDirection);
AutoMatchJoin(box, inputConnectors, context);
return box;
}
///
/// 创建五通连接
///
///
///
///
///
///
///
///
public static FamilyInstance NewFiveLinksFitting(Connector connector1, Connector connector2, Connector connector3, Connector connector4, Connector connector5, JoinContext context)
{
if (context.IsCheckConnected && (connector1.IsConnected || connector2.IsConnected || connector3.IsConnected || connector4.IsConnected || connector5.IsConnected))
{
//已经连接部分
return null;
}
Document doc = connector1.Owner.Document;
List inputConnectors = new List() { connector1, connector2, connector3, connector4, connector5 };
Connector baseConnector = connector1;
Connector useConnector = null;
for (int i = 1; i < inputConnectors.Count; i++)
{
if (!baseConnector.CoordinateSystem.BasisZ.IsParallel(inputConnectors[i].CoordinateSystem.BasisZ))
{
useConnector = inputConnectors[i];
break;
}
}
var line1 = Line.CreateUnbound(baseConnector.Origin, baseConnector.CoordinateSystem.BasisZ);
var line3 = Line.CreateUnbound(useConnector.Origin, useConnector.CoordinateSystem.BasisZ);
var location = line1.GetSpatialIntersection(line3);
FamilySymbol fs = JoinConduitUtils.GetConduitFitting(doc, "线管接线盒-四通");
var box = doc.Create.NewFamilyInstance(location, fs, StructuralType.NonStructural);
#region 根据样式,调整旋转
FittingWrapper fitting = new FittingWrapper(box);
var firstDirection = -line1.Direction;
var secondDirection = -line3.Direction;
var otherConnectors = inputConnectors.Where(c => c != baseConnector && c != useConnector).ToList();
var crossDirection = firstDirection.CrossProduct(secondDirection);
var comValue = -Math.Sqrt(2) / 2;//比较值,夹角cos值小于该值得,变换旋转方向
if (otherConnectors.Any(c => (-c.CoordinateSystem.BasisZ).DotProduct(crossDirection).LessEqual(comValue)))
{
firstDirection = -firstDirection;
}
fitting.Rotate(location, firstDirection, secondDirection);
#endregion
var useMatchDirection = inputConnectors.Select(c => -c.CoordinateSystem.BasisZ).ToList();
JoinConduitUtils.TurnFitting(box, context.JunctionBoxPlace, location, useMatchDirection);
AutoMatchJoin(box, inputConnectors, context);
return box;
}
}
}