493 lines
26 KiB
C#
493 lines
26 KiB
C#
using System.Diagnostics;
|
||
using System.Windows;
|
||
|
||
using Autodesk.Revit.DB;
|
||
using Autodesk.Revit.UI.Selection;
|
||
|
||
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();
|
||
|
||
[RelayCommand]
|
||
private void Closing()
|
||
{
|
||
//关闭窗口退出选择
|
||
if (!CanRunning)
|
||
{
|
||
KeyIntPtrHelper.ActionEsc();
|
||
}
|
||
}
|
||
|
||
[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<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;
|
||
}
|
||
}
|
||
catch (Autodesk.Revit.Exceptions.OperationCanceledException)
|
||
{
|
||
CanRunning = true;
|
||
return;
|
||
}
|
||
|
||
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<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.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 = 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.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 = 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;
|
||
}
|
||
},
|
||
"碰撞处理"
|
||
);
|
||
CanRunning = true;
|
||
});
|
||
}
|
||
}
|
||
|
||
public enum LocationType
|
||
{
|
||
Manual,
|
||
Reference
|
||
}
|
||
|
||
public enum AdjustType
|
||
{
|
||
TwoSide,
|
||
OneSide
|
||
}
|
||
|
||
public enum AdjustDirection
|
||
{
|
||
Up,
|
||
Down,
|
||
Left,
|
||
Right
|
||
}
|