Files
ShrlAlgoToolkit/Sai.RvKits/RvMEP/ClashResolveViewModel.cs
2024-12-22 10:26:12 +08:00

501 lines
27 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 System;
using System.Diagnostics;
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using Autodesk.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Nice3point.Revit.Toolkit.External.Handlers;
namespace Sai.RvKits.RvMEP;
public partial class ClashResolveViewModel : ObservableObject
{
[ObservableProperty]
private AdjustDirection adjustDirection;
[ObservableProperty]
private AdjustType adjustType = AdjustType.OneSide;
/// <summary>
/// 正在执行命令
/// </summary>
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ResolveCommand))]
private bool canRunning = true;
[ObservableProperty]
private double angle = 90.0;
[ObservableProperty]
private double offset = 800;
[ObservableProperty]
private LocationType locationType = LocationType.Manual;
public ActionEventHandler ActionEventHandler { get; } = new();
partial void OnLocationTypeChanged(LocationType value)
{
KeyIntPtrHelper.RaiseEscTwice();
}
[RelayCommand]
private static void Closing()
{
//关闭窗口退出选择
KeyIntPtrHelper.RaiseEscTwice();
}
[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.GetCurve() 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.GetCurve().Project(reference1.GlobalPoint).XYZPoint;
breakPoint2 = mepCurveToBend.GetCurve().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.GetCurve() is Line line
&& !line.Direction.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero)
),
"请选择参照的管线"
);
//参考的管线定位
var referenceMEPCurve = doc.GetElement(reference);
var referenceLine = referenceMEPCurve.GetCurve() as Line;
var reference1 = uidoc.Selection.PickObject(
ObjectType.PointOnElement,
new FuncFilter(
e =>
e is MEPCurve mepCurve and not InsulationLiningBase
&& mepCurve.GetCurve() is Line line
&& !line.Direction.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero)
&& e.Id != referenceMEPCurve!.Id
),
"请选择翻弯的管线或确定单侧翻弯的需要偏移一侧"
);
//翻弯的管线定位
mepCurveToBend = doc.GetElement(reference1) as MEPCurve;
var bendLine = mepCurveToBend.GetCurve() 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<ClosestPointsPairBetweenTwoCurves> 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.GetCurve() 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<Connector>().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.GetCurve() 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.GetCurve();
//用于判断交点是否在偏移后的直线内,避免生成不了
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;
}
}
try
{
//if (originOffsetConnector != null && offsetIntersectPoint.DistanceTo(originOffsetConnector.Origin) > minLength)
if (originOffsetConnector != null)
{
var finalOffsetLine = Line.CreateBound(
offsetIntersectPoint,
originOffsetConnector.Origin
);
baseMepCurve2.SetLocationCurve(finalOffsetLine);
}
var conn1 = ConnectorAssist.GetNearestConnectors(
baseMepCurve1,
angleMepCurve
);
doc.Create.NewElbowFitting(conn1[0], conn1[1]);
var conn2 = ConnectorAssist.GetNearestConnectors(
angleMepCurve,
baseMepCurve2
);
doc.Create.NewElbowFitting(conn2[0], conn2[1]);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
ex.Message.ToLog();
}
}
}
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.GetCurve() 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 = ConnectorAssist.GetNearestConnectors(
baseMepCurve1,
firstMepCurve
);
doc.Create.NewElbowFitting(connectors1[0], connectors1[1]);
var connectors4 = ConnectorAssist.GetNearestConnectors(
thirdMepCurve,
baseMepCurve2
);
doc.Create.NewElbowFitting(connectors4[0], connectors4[1]);
}
catch (Autodesk.Revit.Exceptions.InvalidOperationException)
{
MessageBox.Show("角度过大或过小", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
ex.Message.ToLog();
}
try
{
var connectors2 = ConnectorAssist.GetNearestConnectors(
firstMepCurve,
secondMepCurve
);
doc.Create.NewElbowFitting(connectors2[0], connectors2[1]);
var connectors3 = ConnectorAssist.GetNearestConnectors(
secondMepCurve,
thirdMepCurve
);
doc.Create.NewElbowFitting(connectors3[0], connectors3[1]);
}
catch (Autodesk.Revit.Exceptions.InvalidOperationException)
{
MessageBox.Show("角度过大或过小", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
ex.Message.ToLog();
}
}
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
}