using System.Diagnostics;
using System.Linq;
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using Nice3point.Revit.Toolkit.External.Handlers;
using Szmedi.RvKits.Assists;
namespace Szmedi.RvKits.MEPTools;
public partial class ClashResolveViewModel : ObservableObject
{
//public ClashResolveViewModel(UIApplication uiapp)
//{
// this.uiapp = uiapp;
//}
//private readonly UIApplication uiapp;
[ObservableProperty] private AdjustDirection adjustDirection;
[ObservableProperty] private AdjustType adjustType;
///
/// 正在执行命令
///
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ResolveCommand))]
private bool canRunning = true;
[ObservableProperty] private double angle = 90.0;
[ObservableProperty] private double offset = 800;
[ObservableProperty] private LocationType locationType = MEPTools.LocationType.Manual;
public ActionEventHandler ActionEventHandler { get; } = new();
partial void OnLocationTypeChanged(LocationType value)
{
CommandAssists.ExitCurrentCommand();
}
[RelayCommand]
private void Closing()
{
CommandAssists.ExitCurrentCommand();
}
[RelayCommand(CanExecute = nameof(CanRunning))]
private void Resolve()
{
CanRunning = false;
ActionEventHandler.Raise(uiapp =>
{
var uidoc = uiapp.ActiveUIDocument;
var doc = uidoc.Document;
MEPCurve mepCurveToBend = default;
XYZ breakPoint1 = default;
XYZ breakPoint2 = default;
doc.Invoke(
ts =>
{
try
{
switch (LocationType)
{
case LocationType.Manual:
{
var reference1 = uidoc.Selection.PickObject(
ObjectType.PointOnElement,
new FuncFilter(
e =>
e is MEPCurve mepCurve and not InsulationLiningBase
&& mepCurve.GetLocCurve() is Line line
&& !line.Direction.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero)
),
"请选择翻弯的管线上的点"
);
mepCurveToBend = doc.GetElement(reference1) as MEPCurve;
if (mepCurveToBend.Pinned)
{
MessageBox.Show("错误", "请解锁图元");
CanRunning = true;
return;
}
var reference2 = uidoc.Selection.PickObject(
ObjectType.PointOnElement,
new FuncFilter(e => e.Id == mepCurveToBend!.Id),
"请选择另一个翻弯管线上的点或确定单侧翻弯的需要偏移一侧"
);
//两个断点,breakPoint1距离原直线起点近,反向时,交换值
breakPoint1 = mepCurveToBend.GetLocCurve().Project(reference1.GlobalPoint).XYZPoint;
breakPoint2 = mepCurveToBend.GetLocCurve().Project(reference2.GlobalPoint).XYZPoint;
}
break;
case LocationType.Reference:
{
var reference = uidoc.Selection.PickObject(
ObjectType.Element,
new FuncFilter(
e =>
e is MEPCurve mepCurve and not InsulationLiningBase
&& mepCurve.GetLocCurve() is Line line
&& !line.Direction.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero)
),
"请选择参照的管线"
);
//参考的管线定位
var referenceMEPCurve = doc.GetElement(reference);
var referenceLine = referenceMEPCurve.GetLocCurve() as Line;
var reference1 = uidoc.Selection.PickObject(
ObjectType.PointOnElement,
new FuncFilter(
e =>
e is MEPCurve mepCurve and not InsulationLiningBase
&& mepCurve.GetLocCurve() is Line line
&& !line.Direction.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero)
&& e.Id != referenceMEPCurve!.Id
),
"请选择翻弯的管线或确定单侧翻弯的需要偏移一侧"
);
//翻弯的管线定位
mepCurveToBend = doc.GetElement(reference1) as MEPCurve;
var bendLine = mepCurveToBend.GetLocCurve() as Line;
var result = bendLine!.Intersect(referenceLine, out var array);
XYZ intersectPoint = default;
switch (result)
{
case SetComparisonResult.Overlap:
intersectPoint = array.get_Item(0).XYZPoint;
break;
case SetComparisonResult.Disjoint:
{
IList points =
[];
bendLine.ComputeClosestPoints(referenceLine, true, true, false, out points);
var point = points.FirstOrDefault()?.XYZPointOnFirstCurve;
if (bendLine.IsInsideEx(point, 0.2))
{
intersectPoint = point;
}
break;
}
}
breakPoint1 = intersectPoint - (bendLine.Direction * Offset / 304.8);
breakPoint2 = intersectPoint + (bendLine.Direction * Offset / 304.8);
if (
reference1.GlobalPoint.DistanceTo(breakPoint1)
< reference1.GlobalPoint.DistanceTo(breakPoint2)
) //距离近的是breakpoint2
{
(breakPoint1, breakPoint2) = (breakPoint2, breakPoint1);
}
if (
intersectPoint == default
|| !bendLine.IsInsideEx(breakPoint1, 0.2)
|| !bendLine.IsInsideEx(breakPoint2, 0.2)
)
{
return;
}
}
break;
}
var originBaseLine = mepCurveToBend.GetLocCurve() as Line;
var baseLine = originBaseLine!.Clone() as Line;
//原管线的方向
var direction = baseLine?.Direction;
var startPoint = baseLine!.GetEndPoint(0);
var endPoint = baseLine.GetEndPoint(1);
var breakLine = Line.CreateBound(breakPoint1, breakPoint2);
//var minLength = mepCurveToBend.GetConnectors().OfType().FirstOrDefault().GetExtensionLength();
//if (breakLine.Length < minLength)
//{
// return;
//}
var translateDirection = AdjustDirection switch
{
AdjustDirection.Up => XYZ.BasisZ,
AdjustDirection.Down => -XYZ.BasisZ,
AdjustDirection.Left => XYZ.BasisZ.CrossProduct(breakLine.Direction),
AdjustDirection.Right => -XYZ.BasisZ.CrossProduct(breakLine.Direction),
_ => null
};
switch (AdjustType)
{
case AdjustType.OneSide:
{
MEPCurve baseMepCurve1;
MEPCurve baseMepCurve2;
var newMepCurveId = mepCurveToBend.BreakByPoint(breakPoint1);
var newMepCurve = doc.GetElement(newMepCurveId) as MEPCurve;
//baseMepCurve2始终是打断后生成的新管线,并且是需要偏移的管线
if ((newMepCurve.GetLocCurve() as Line).IsInsideEx(breakPoint2))
{
baseMepCurve1 = mepCurveToBend;
baseMepCurve2 = newMepCurve;
}
else
{
baseMepCurve1 = newMepCurve;
baseMepCurve2 = mepCurveToBend;
}
//移动新生成的管线
ElementTransformUtils.MoveElement(
doc,
baseMepCurve2.Id,
translateDirection * Offset / 304.8
);
doc.Regenerate();
////偏移变换
//var translateTransform = Transform.CreateTranslation(translateDirection * Offset / 304.8);
//
//var originOffsetUnboundLine = baseLine.CreateTransformed(translateTransform) as Line;
//需要偏移直线定位线
var originOffsetUnboundLine = baseMepCurve2.GetLocCurve();
//用于判断交点是否在偏移后的直线内,避免生成不了
var originOffsetLine = originOffsetUnboundLine.Clone() as Line;
originOffsetUnboundLine.MakeUnbound();
var radian = Angle.ToRadian();
//反向时角度取补角
if (breakLine.Direction.IsAlmostEqualTo(-direction))
{
radian = (180 - Angle).ToRadian();
}
var rotation = Transform.CreateRotationAtPoint(
direction.CrossProduct(translateDirection),
radian,
breakPoint1
);
//因为原来的管线可能被移动,所以用baseline的克隆线
var unboundAngleLine = baseLine.CreateTransformed(rotation) as Line;
unboundAngleLine!.MakeUnbound();
//偏移管线与倾斜管线交点
var offsetIntersectPoint = originOffsetUnboundLine.IntersectionPoint(unboundAngleLine);
var isInside = originOffsetLine.IsInsideEx(offsetIntersectPoint); //当角度线于偏移直线交点在偏移直线内时
//var flag2 = breakPoint1!.DistanceTo(offsetIntersectPoint) > minLength; //偏移间距大于一定一定值
if (!isInside)
{
MessageBox.Show("管线偏移角度过大或过小,导致交点不在偏移的管线范围内");
break;
}
//if (!flag2)
//{
// MessageBox.Show("管线偏移距离过近");
// break;
//}
//if (isInside)
else
{
var angleLine = Line.CreateBound(breakPoint1, offsetIntersectPoint);
var angleMepCurve = mepCurveToBend.CopyAndSetLocationCurve(angleLine);
//拿到角度管的偏移交点处的连接件
var angleMEPCurveIntersectPointConnector = angleMepCurve
.GetConnectors(true)
.GetNearestConnector(offsetIntersectPoint);
//移动后的管线原有的连接件中,不用连接角度曲线的连接件,用来确定移动后管线的定位线
Connector originOffsetConnector = null;
foreach (Connector conn in baseMepCurve2.GetConnectors(true))
{
//90度时,偏移管线的两个连接都是90度,但要选择偏移管原有的,非打断处的连接件
if (
conn.CoordinateSystem.BasisZ.DotProduct(
angleMEPCurveIntersectPointConnector.CoordinateSystem.BasisZ
) >= 0
&& !offsetIntersectPoint.IsAlmostEqualTo(conn.Origin)
) //锐角
{
originOffsetConnector = conn;
break;
}
}
//if (originOffsetConnector != null && offsetIntersectPoint.DistanceTo(originOffsetConnector.Origin) > minLength)
if (originOffsetConnector != null)
{
var finalOffsetLine = Line.CreateBound(
offsetIntersectPoint,
originOffsetConnector.Origin
);
baseMepCurve2.SetLocationCurve(finalOffsetLine);
}
try
{
var conn1 = ConnectorExtensions.GetNearestConnectors(
baseMepCurve1,
angleMepCurve
);
doc.Create.NewElbowFitting(conn1[0], conn1[1]);
var conn2 = ConnectorExtensions.GetNearestConnectors(
angleMepCurve,
baseMepCurve2
);
doc.Create.NewElbowFitting(conn2[0], conn2[1]);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
LogAssists.WriteLog(ex.Message);
}
}
}
break;
case AdjustType.TwoSide:
{
MEPCurve baseMepCurve1;
MEPCurve baseMepCurve2;
//选择点的方向与原直线方向相反时,使得breakPoint1~breakPoint2始终与原直线同向
if (breakLine.Direction.IsAlmostEqualTo(-baseLine.Direction))
{
//交换值
(breakPoint1, breakPoint2) = (breakPoint2, breakPoint1);
}
//doc.Regenerate();
//偏移变换
var translateTransform = Transform.CreateTranslation(
translateDirection * Offset / 304.8
);
//偏移直线
var offsetLine = baseLine.CreateTransformed(translateTransform) as Line;
offsetLine!.MakeUnbound();
//根据角度旋转的矩阵变换,创建变换后的直线
//第一个交点
var rotateTransform1 = Transform.CreateRotationAtPoint(
direction.CrossProduct(translateDirection),
Angle.ToRadian(),
breakPoint1
);
var angleLine1 = baseLine.CreateTransformed(rotateTransform1) as Line;
angleLine1!.MakeUnbound();
var offsetPoint1 = offsetLine.IntersectionPoint(angleLine1);
//根据角度旋转的矩阵变换,创建变换后的直线
//第二个交点
var rotateTransform2 = Transform.CreateRotationAtPoint(
direction.CrossProduct(translateDirection),
(180 - Angle).ToRadian(),
breakPoint2
);
var angleLine2 = baseLine.CreateTransformed(rotateTransform2) as Line;
angleLine2!.MakeUnbound();
var offsetPoint2 = offsetLine.IntersectionPoint(angleLine2);
var b = Line.CreateBound(offsetPoint1, offsetPoint2);
if (b.Direction.IsAlmostEqualTo(-baseLine.Direction))
{
Debug.WriteLine("翻弯的管线交叉");
MessageBox.Show(
"两点距离太近,无法翻弯",
"提醒",
MessageBoxButton.OK,
MessageBoxImage.Information
);
ts.RollBack();
break;
}
var newMepCurveId = mepCurveToBend.BreakByPoint(breakPoint1);
var newMepCurve = doc.GetElement(newMepCurveId) as MEPCurve;
//判断打断生成的新管线的位置是原管线的前端还是后端
if ((newMepCurve.GetLocCurve() as Line).IsInsideEx(breakPoint2)) //后端
{
baseMepCurve1 = mepCurveToBend;
baseMepCurve2 = newMepCurve;
}
else
{
baseMepCurve1 = newMepCurve;
baseMepCurve2 = mepCurveToBend;
}
//第一条管线
var newBaseLine1 = Line.CreateBound(startPoint, breakPoint1);
//第二条管线
var newBaseLine2 = Line.CreateBound(breakPoint2, endPoint);
baseMepCurve1.SetLocationCurve(newBaseLine1);
baseMepCurve2.SetLocationCurve(newBaseLine2);
//生成的偏移线方向相反时,会导致两条翻弯的管线交叉
//第一根翻弯管
var firstMepCurve = mepCurveToBend.CopyAndSetLocationCurve(
Line.CreateBound(breakPoint1, offsetPoint1)
);
//偏移管
var secondMepCurve = mepCurveToBend.CopyAndSetLocationCurve(
Line.CreateBound(offsetPoint1, offsetPoint2)
);
//第二根翻弯管
var thirdMepCurve = mepCurveToBend.CopyAndSetLocationCurve(
Line.CreateBound(offsetPoint2, breakPoint2)
);
doc.Regenerate();
try
{
var connectors1 = ConnectorExtensions.GetNearestConnectors(
baseMepCurve1,
firstMepCurve
);
doc.Create.NewElbowFitting(connectors1[0], connectors1[1]);
var connectors4 = ConnectorExtensions.GetNearestConnectors(
thirdMepCurve,
baseMepCurve2
);
doc.Create.NewElbowFitting(connectors4[0], connectors4[1]);
}
catch (Autodesk.Revit.Exceptions.InvalidOperationException)
{
MessageBox.Show("角度过大或过小", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
LogAssists.WriteLog(ex.Message);
}
try
{
var connectors2 = ConnectorExtensions.GetNearestConnectors(
firstMepCurve,
secondMepCurve
);
doc.Create.NewElbowFitting(connectors2[0], connectors2[1]);
var connectors3 = ConnectorExtensions.GetNearestConnectors(
secondMepCurve,
thirdMepCurve
);
doc.Create.NewElbowFitting(connectors3[0], connectors3[1]);
}
catch (Autodesk.Revit.Exceptions.InvalidOperationException)
{
MessageBox.Show("角度过大或过小", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
LogAssists.WriteLog(ex.Message);
}
}
break;
}
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException)
{
CanRunning = true;
return;
}
catch (Exception)
{
CanRunning = true;
return;
}
},
"碰撞处理"
);
CanRunning = true;
});
}
}
public enum LocationType
{
Manual,
Reference
}
public enum AdjustType
{
TwoSide,
OneSide
}
public enum AdjustDirection
{
Up,
Down,
Left,
Right
}