多项功能优化

This commit is contained in:
GG Z
2024-12-22 10:26:12 +08:00
parent 77655c9ef5
commit 83b846f15f
66 changed files with 5424 additions and 2927 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -44,10 +44,15 @@ namespace Sai.RvKits.RvMEP
e is not InsulationLiningBase &&
e is not FlexDuct &&
e is not FlexPipe &&
!(e.GetLocCurve() as Line).Direction.IsParallelTo(XYZ.BasisZ)),
!(e.GetCurve() as Line).Direction.IsParallelTo(XYZ.BasisZ)),
"请选择要整理的管线")
.Select(e => e.Id)
.ToList();
if (ids.Count <= 1)
{
return;
}
var baseId = uidoc.Selection
.PickObject(
Autodesk.Revit.UI.Selection.ObjectType.Element,
@@ -55,13 +60,8 @@ namespace Sai.RvKits.RvMEP
"请选择基准管线")
.ElementId;
if (ids.Count <= 1)
{
return;
}
var baseElement = doc.GetElement(baseId);
var baseLine = baseElement.GetLocCurve() as Line;
var baseLine = baseElement.GetCurve() as Line;
baseLine.MakeUnbound();
if (IsHorizon)
{
@@ -72,10 +72,10 @@ namespace Sai.RvKits.RvMEP
foreach (var id in ids)
{
var elem = doc.GetElement(id);
var l = elem.GetLocCurve() as Line;
var l = elem.GetCurve() as Line;
if (!l.Direction.IsParallelTo(baseLine.Direction))
{
MessageBox.Show("错误", "所选管线不平行");
MessageBox.Show("所选管线不平行", "错误");
return;
}
var d = baseLine.Distance(l.Origin);
@@ -225,14 +225,14 @@ namespace Sai.RvKits.RvMEP
{
//通过LocationCurve确定相对位置关系
var orderIds = ids.Select(id => doc.GetElement(id) as MEPCurve)
.OrderBy(c => (c.GetLocCurve() as Line).Origin.Z).Select(e => e.Id).ToList();
.OrderBy(c => (c.GetCurve() as Line).Origin.Z).Select(e => e.Id).ToList();
//中心距
if (MEPCurveCenter)
{
var baseIndex = orderIds.IndexOf(baseId);
for (var i = 0; i < orderIds.Count; i++)
{
var currentZ = (doc.GetElement(orderIds[i]).GetLocCurve() as Line).Origin.Z;
var currentZ = (doc.GetElement(orderIds[i]).GetCurve() as Line).Origin.Z;
var dis = Math.Abs(baseLine.Origin.Z - currentZ);
var actualDis = Math.Abs((baseIndex - i)) * Gap / 304.8;
//判断方向
@@ -255,7 +255,7 @@ namespace Sai.RvKits.RvMEP
{
ids.Remove(baseId);
var groups = ids.Select(id => doc.GetElement(id) as MEPCurve)
.OrderBy(c => (c.GetLocCurve() as Line).Origin.Z).GroupBy(e => (e.GetLocCurve() as Line).Origin.Z > baseLine.Origin.Z);
.OrderBy(c => (c.GetCurve() as Line).Origin.Z).GroupBy(e => (e.GetCurve() as Line).Origin.Z > baseLine.Origin.Z);
var baseTop = baseElement.get_BoundingBox(null).Max.Z;
var baseBottom = baseElement.get_BoundingBox(null).Min.Z;
if (baseElement is Pipe or Duct)

View File

@@ -1,4 +1,5 @@
using System.Collections;
using System.Diagnostics;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Electrical;
@@ -9,6 +10,8 @@ using Autodesk.Revit.UI.Selection;
using Nice3point.Revit.Toolkit.External;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using Sai.Toolkit.Revit.Helpers;
namespace Sai.RvKits.RvMEP;
@@ -24,7 +27,11 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
var elemIds = UiDocument.Selection.GetElementIds();
if (elemIds.Count == 0)
{
var reference = UiDocument.Selection.PickObject(ObjectType.Element, new FuncFilter(e => e is FamilyInstance ins && ins.MEPModel.ConnectorManager != null), "请选择族实例");
var reference = UiDocument.Selection
.PickObject(
ObjectType.Element,
new FuncFilter(e => e is FamilyInstance ins && ins.GetConnectors(true).Size > 0),
"请选择族实例");
elemIds.Add(Document.GetElement(reference).Id);
}
@@ -34,15 +41,13 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
var cableTrayTypeId = Document.OfClass<CableTrayType>().FirstElementId();
var conduitTypeId = Document.OfClass<ConduitType>().FirstElementId();
var filteredElementCollector = Document.OfClass<DuctType>();
var ductTypeCollector = Document.OfClass<DuctType>().Cast<DuctType>();
var roundDuctTypeId = ElementId.InvalidElementId;
var rectangleDuctTypeId = ElementId.InvalidElementId;
var ovalDuctTypeId = ElementId.InvalidElementId;
foreach (var element2 in filteredElementCollector)
//设置默认的风管类型
foreach (var ductType in ductTypeCollector)
{
var ductType = (DuctType)element2;
if (ductType.FamilyName == "圆形风管" || ductType.FamilyName.Contains("Round Duct"))
{
roundDuctTypeId = ductType.Id;
@@ -65,7 +70,7 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
}
CableTray referCabTray = null;
Conduit referConduit = null;
//拿到连接的管线的类型
//根据连接件拿到连接的管线的类型
foreach (Connector conn in elem.GetConnectors())
{
if (conn.IsConnected)
@@ -117,8 +122,8 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
Document.Invoke(
_ =>
{
var conns = elem.GetConnectors(true);
foreach (Connector connector in conns)
var connectors = elem.GetConnectors(true);
foreach (Connector connector in connectors)
{
Element element = null;
if (elem.Category.Id.IntegerValue == (int)BuiltInCategory.OST_FabricationPipework)
@@ -166,6 +171,7 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
}
var extensionLength = connector.GetExtensionLength();
var origin = connector.Origin;
//延伸后的点
var xyz2 = origin + (extensionLength * connector.CoordinateSystem.BasisZ);
var levelId = elem.LevelId;
if (levelId == ElementId.InvalidElementId)
@@ -193,43 +199,43 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
break;
//风管
case Domain.DomainHvac:
var mechanicalSystemTypes = new FilteredElementCollector(Document)
.OfClass(typeof(MechanicalSystemType))
.Cast<MechanicalSystemType>();
//风管系统类型
var mechanicalSystem =
connector.MEPSystem == null
? connector.DuctSystemType switch
{
//送风
DuctSystemType.SupplyAir
=> mechanicalSystemTypes.FirstOrDefault(
pst =>
pst.SystemClassification
== Autodesk.Revit.DB.MEPSystemClassification.SupplyAir
),
//回风
DuctSystemType.ReturnAir
=> mechanicalSystemTypes.FirstOrDefault(
pst =>
pst.SystemClassification
== Autodesk.Revit.DB.MEPSystemClassification.ReturnAir
),
//排风
DuctSystemType.ExhaustAir
=> mechanicalSystemTypes.FirstOrDefault(
pst =>
pst.SystemClassification
== Autodesk.Revit.DB.MEPSystemClassification.ExhaustAir
),
_
=> mechanicalSystemTypes.FirstOrDefault(
pst =>
pst.SystemClassification
== Autodesk.Revit.DB.MEPSystemClassification.OtherAir
)
}
: Document.GetElement(connector.MEPSystem.GetTypeId()) as MechanicalSystemType;
//var mechanicalSystemTypes = new FilteredElementCollector(Document)
// .OfClass(typeof(MechanicalSystemType))
// .Cast<MechanicalSystemType>();
////风管系统类型
//var mechanicalSystem =
// connector.MEPSystem == null
// ? connector.DuctSystemType switch
// {
// //送风
// DuctSystemType.SupplyAir
// => mechanicalSystemTypes.FirstOrDefault(
// pst =>
// pst.SystemClassification
// == Autodesk.Revit.DB.MEPSystemClassification.SupplyAir
// ),
// //回风
// DuctSystemType.ReturnAir
// => mechanicalSystemTypes.FirstOrDefault(
// pst =>
// pst.SystemClassification
// == Autodesk.Revit.DB.MEPSystemClassification.ReturnAir
// ),
// //排风
// DuctSystemType.ExhaustAir
// => mechanicalSystemTypes.FirstOrDefault(
// pst =>
// pst.SystemClassification
// == Autodesk.Revit.DB.MEPSystemClassification.ExhaustAir
// ),
// _
// => mechanicalSystemTypes.FirstOrDefault(
// pst =>
// pst.SystemClassification
// == Autodesk.Revit.DB.MEPSystemClassification.OtherAir
// )
// }
// : Document.GetElement(connector.MEPSystem.GetTypeId()) as MechanicalSystemType;
//if (new FilteredElementCollector(Document).OfClass(typeof(DuctType)).FirstElement() is not DuctType)
//{
@@ -242,10 +248,9 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
case ConnectorProfileType.Round:
element = Duct.Create(
Document,
mechanicalSystem.Id,
roundDuctTypeId,
levelId,
origin,
connector,
xyz2
);
@@ -256,31 +261,33 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
case ConnectorProfileType.Rectangular:
element = Duct.Create(
Document,
mechanicalSystem.Id,
rectangleDuctTypeId,
levelId,
origin,
connector,
xyz2
);
Document.Regenerate();
element.get_Parameter(BuiltInParameter.RBS_CURVE_WIDTH_PARAM).Set(connector.Width);
element.get_Parameter(BuiltInParameter.RBS_CURVE_HEIGHT_PARAM).Set(connector.Height);
var width = Math.Max(connector.Width, connector.Height);
var height = Math.Min(connector.Width, connector.Height);
element.get_Parameter(BuiltInParameter.RBS_CURVE_WIDTH_PARAM).Set(width);
element.get_Parameter(BuiltInParameter.RBS_CURVE_HEIGHT_PARAM).Set(height);
break;
case ConnectorProfileType.Oval:
element = Duct.Create(Document, mechanicalSystem.Id, ovalDuctTypeId, levelId, origin, xyz2);
element = Duct.Create(Document, ovalDuctTypeId, levelId, connector, xyz2);
Document.Regenerate();
element.get_Parameter(BuiltInParameter.RBS_CURVE_WIDTH_PARAM).Set(connector.Width);
element.get_Parameter(BuiltInParameter.RBS_CURVE_HEIGHT_PARAM).Set(connector.Height);
var w = Math.Max(connector.Width, connector.Height);
var h = Math.Min(connector.Width, connector.Height);
element.get_Parameter(BuiltInParameter.RBS_CURVE_WIDTH_PARAM).Set(w);
element.get_Parameter(BuiltInParameter.RBS_CURVE_HEIGHT_PARAM).Set(h);
break;
default:
element = Duct.Create(
Document,
mechanicalSystem.Id,
roundDuctTypeId,
levelId,
origin,
connector,
xyz2
);
break;
@@ -297,7 +304,7 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
.Cast<PipingSystemType>();
if (pipeTypeId == ElementId.InvalidElementId)
{
pipeTypeId = Document.OfClass<PipeType>().WhereElementIsElementType().FirstElementId();
pipeTypeId = Document.OfClass<PipeType>().FirstElementId();
}
var system = connector.MEPSystem;
var pipingSystemType =
@@ -435,9 +442,11 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
break;
case ConnectorProfileType.Rectangular:
var width = Math.Max(connector.Width, connector.Height);
var height = Math.Min(connector.Width, connector.Height);
element = CableTray.Create(Document, cableTrayTypeId, origin, xyz2, levelId);
element.get_Parameter(BuiltInParameter.RBS_CABLETRAY_WIDTH_PARAM).Set(connector.Width);
element.get_Parameter(BuiltInParameter.RBS_CABLETRAY_HEIGHT_PARAM).Set(connector.Height);
element.get_Parameter(BuiltInParameter.RBS_CABLETRAY_WIDTH_PARAM).Set(width);
element.get_Parameter(BuiltInParameter.RBS_CABLETRAY_HEIGHT_PARAM).Set(height);
if (referCabTray != null)
{
var value = referCabTray.get_Parameter(
@@ -460,15 +469,81 @@ internal class BloomConnectorCmd : ExternalCommand //根据连接件创建一根
continue;
}
var curve = element as MEPCurve;
var manager = curve.ConnectorManager;
var lter = manager.UnusedConnectors.ForwardIterator();
while (lter.MoveNext())
var conns = curve.ConnectorManager.UnusedConnectors.Cast<Connector>();
foreach (var connect in conns)
{
var connect = lter.Current as Connector;
if (connect.Origin.IsAlmostEqualTo(connector.Origin))
{
Debug.WriteLine("新建管线:");
Debug.WriteLine(connect.CoordinateSystem.BasisX);
Debug.WriteLine(connect.CoordinateSystem.BasisY);
Debug.WriteLine(connect.CoordinateSystem.BasisZ);
Debug.WriteLine("管件:");
Debug.WriteLine(connector.CoordinateSystem.BasisX);
Debug.WriteLine(connector.CoordinateSystem.BasisY);
Debug.WriteLine(connector.CoordinateSystem.BasisZ);
var isStandMEPCurve = connect.CoordinateSystem.BasisZ.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero);
if (isStandMEPCurve)
{
var angleX = Math.Acos(connect.CoordinateSystem.BasisX.DotProduct(connector.CoordinateSystem.BasisX));
Debug.WriteLine(angleX);
var crossProduct = connect.CoordinateSystem.BasisX
.CrossProduct(connector.CoordinateSystem.BasisX).Normalize();
var angle = Math.Acos(connect.CoordinateSystem.BasisX.DotProduct(connector.CoordinateSystem.BasisX));
if (crossProduct.IsAlmostEqualTo(XYZ.Zero))
{
if (connect.CoordinateSystem.BasisX
.IsAlmostEqualTo(connector.CoordinateSystem.BasisX))
{
ElementTransformUtils.RotateElement(
Document,
element.Id,
Line.CreateUnbound(origin, XYZ.BasisZ),
Math.PI / 2);
}
}
else
{
if (angleX > Math.PI / 2)
{
ElementTransformUtils.RotateElement(
Document,
element.Id,
Line.CreateUnbound(origin, XYZ.BasisZ),
angleX - Math.PI / 2);
}
else
{
ElementTransformUtils.RotateElement(
Document,
element.Id,
Line.CreateUnbound(origin, XYZ.BasisZ),
Math.PI / 2 - angleX);
}
}
//if (Math.Abs(angleX - Math.PI / 2) > 0.001)
//{
// ElementTransformUtils.RotateElement(
// Document,
// element.Id,
// Line.CreateUnbound(origin, crossProduct),
// angleX - Math.PI / 2
// );
// Debug.WriteLine("旋转后新建管线:");
// Debug.WriteLine(connect.CoordinateSystem.BasisX);
// Debug.WriteLine(connect.CoordinateSystem.BasisY);
// Debug.WriteLine(connect.CoordinateSystem.BasisZ);
// Document.Regenerate();
// angle = Math.Acos(connect.CoordinateSystem.BasisX.DotProduct(connector.CoordinateSystem.BasisX));
// Debug.WriteLine(nameof(angle) + angle);
//}
}
var conn = elem.GetConnectors(true).GetNearestConnector(connector.Origin);
conn.ConnectTo(connect);
if (!connect.IsConnectedTo(connector))
{
connect.ConnectTo(connector);
}
break;
}
}

View File

@@ -32,14 +32,14 @@ public class BreakMEPCurveCmd : ExternalCommand
var pointPicked = refer1.GlobalPoint;
var mepCurve = UiDocument.Document.GetElement(refer1) as MEPCurve;
var loc = mepCurve.GetLocCurve() as Line;
var loc = mepCurve.GetCurve() as Line;
var d = loc.Direction;
var halfGap = 20 / 304.8;
var point1 = pointPicked - d * halfGap;
var point2 = pointPicked + d * halfGap;
var breakPoint1 = mepCurve.GetLocCurve().Project(point1).XYZPoint;
var breakPoint2 = mepCurve.GetLocCurve().Project(point2).XYZPoint;
var breakPoint1 = mepCurve.GetCurve().Project(point1).XYZPoint;
var breakPoint2 = mepCurve.GetCurve().Project(point2).XYZPoint;
if (!loc.IsInsideEx(breakPoint1, halfGap) || !loc.IsInsideEx(breakPoint2, halfGap))
{
MessageBox.Show("打断点距离管线端点太近");

View File

@@ -48,8 +48,8 @@ public class CableLayoutCmd : ExternalCommand
var conduit = Conduit.Create(
Document,
conduitType.Id,
mepCurve.GetLocCurve().GetEndPoint(0),
mepCurve.GetLocCurve().GetEndPoint(1),
mepCurve.GetCurve().GetEndPoint(0),
mepCurve.GetCurve().GetEndPoint(1),
mepCurve.LevelId
);
firstConduits.Add(conduit);
@@ -71,7 +71,7 @@ public class CableLayoutCmd : ExternalCommand
foreach (var conduit in firstConduits)
{
var line = conduit.GetLocCurve() as Line;
var line = conduit.GetCurve() as Line;
var direction = line!.Direction.CrossProduct(XYZ.BasisZ);
var id = ElementTransformUtils.CopyElement(Document, conduit.Id, direction * offest).FirstOrDefault();
var conduitCopied = Document.GetElement(id) as Conduit;

View File

@@ -10,8 +10,16 @@ namespace Sai.RvKits.RvMEP;
[Regeneration(RegenerationOption.Manual)]
public class ClashResolveCmd : ExternalCommand
{
public override void Execute()
{
WinDialogHelper.ShowModeless<ClashResolveView>(new ClashResolveViewModel());
}
public override void Execute()
{
try
{
WinDialogHelper.ShowModeless<ClashResolveView>(new ClashResolveViewModel());
}
catch (Exception)
{
throw;
}
}
}

View File

@@ -89,8 +89,14 @@
<ex:StackPanelEx Orientation="Horizontal" Spacing="5">
<GroupBox ex:StackPanelEx.Fill="Fill" Header="参考方式">
<UniformGrid Rows="1">
<RadioButton Content="手动" IsChecked="{Binding LocationType, ConverterParameter={x:Static local:LocationType.Manual}, Converter={StaticResource ComparisonConverter}}" />
<RadioButton Content="参考" IsChecked="{Binding LocationType, ConverterParameter={x:Static local:LocationType.Reference}, Converter={StaticResource ComparisonConverter}}" />
<RadioButton
Content="手动"
IsChecked="{Binding LocationType, ConverterParameter={x:Static local:LocationType.Manual}, Converter={StaticResource ComparisonConverter}}"
ToolTip="手动选取两点" />
<RadioButton
Content="参考"
IsChecked="{Binding LocationType, ConverterParameter={x:Static local:LocationType.Reference}, Converter={StaticResource ComparisonConverter}}"
ToolTip="选择相交的两条管线,将翻弯管线偏移一定距离" />
</UniformGrid>
</GroupBox>
<GroupBox ex:StackPanelEx.Fill="Fill" Header="调整方式">

View File

@@ -1,8 +1,10 @@
using System.Diagnostics;
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;
@@ -36,15 +38,15 @@ public partial class ClashResolveViewModel : ObservableObject
private LocationType locationType = LocationType.Manual;
public ActionEventHandler ActionEventHandler { get; } = new();
partial void OnLocationTypeChanged(LocationType value)
{
KeyIntPtrHelper.RaiseEscTwice();
}
[RelayCommand]
private void Closing()
private static void Closing()
{
//关闭窗口退出选择
if (!CanRunning)
{
KeyIntPtrHelper.ActionEsc();
}
KeyIntPtrHelper.RaiseEscTwice();
}
[RelayCommand(CanExecute = nameof(CanRunning))]
@@ -74,7 +76,7 @@ public partial class ClashResolveViewModel : ObservableObject
new FuncFilter(
e =>
e is MEPCurve mepCurve and not InsulationLiningBase
&& mepCurve.GetLocCurve() is Line line
&& mepCurve.GetCurve() is Line line
&& !line.Direction.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero)
),
"请选择翻弯的管线上的点"
@@ -92,8 +94,8 @@ public partial class ClashResolveViewModel : ObservableObject
"请选择另一个翻弯管线上的点或确定单侧翻弯的需要偏移一侧"
);
//两个断点,breakPoint1距离原直线起点近反向时交换值
breakPoint1 = mepCurveToBend.GetLocCurve().Project(reference1.GlobalPoint).XYZPoint;
breakPoint2 = mepCurveToBend.GetLocCurve().Project(reference2.GlobalPoint).XYZPoint;
breakPoint1 = mepCurveToBend.GetCurve().Project(reference1.GlobalPoint).XYZPoint;
breakPoint2 = mepCurveToBend.GetCurve().Project(reference2.GlobalPoint).XYZPoint;
}
break;
case LocationType.Reference:
@@ -104,21 +106,21 @@ public partial class ClashResolveViewModel : ObservableObject
new FuncFilter(
e =>
e is MEPCurve mepCurve and not InsulationLiningBase
&& mepCurve.GetLocCurve() is Line line
&& mepCurve.GetCurve() is Line line
&& !line.Direction.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero)
),
"请选择参照的管线"
);
//参考的管线定位
var referenceMEPCurve = doc.GetElement(reference);
var referenceLine = referenceMEPCurve.GetLocCurve() as Line;
var referenceLine = referenceMEPCurve.GetCurve() as Line;
var reference1 = uidoc.Selection.PickObject(
ObjectType.PointOnElement,
new FuncFilter(
e =>
e is MEPCurve mepCurve and not InsulationLiningBase
&& mepCurve.GetLocCurve() is Line line
&& mepCurve.GetCurve() is Line line
&& !line.Direction.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero)
&& e.Id != referenceMEPCurve!.Id
),
@@ -126,7 +128,7 @@ public partial class ClashResolveViewModel : ObservableObject
);
//翻弯的管线定位
mepCurveToBend = doc.GetElement(reference1) as MEPCurve;
var bendLine = mepCurveToBend.GetLocCurve() as Line;
var bendLine = mepCurveToBend.GetCurve() as Line;
var result = bendLine!.Intersect(referenceLine, out var array);
@@ -172,296 +174,302 @@ public partial class ClashResolveViewModel : ObservableObject
}
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;
}
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
catch (Exception)
{
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;
return;
}
},
"碰撞处理"

View File

@@ -119,7 +119,7 @@ public class CorrectMEPCurveSlopeCmd : ExternalCommand
case Duct:
{
var param = mepCurve.get_Parameter(BuiltInParameter.RBS_DUCT_SLOPE);
var loc = mepCurve.GetLocCurve() as Line;
var loc = mepCurve.GetCurve() as Line;
isError = param.HasValue && ((param.AsDouble() < 0.025 && param.AsDouble() > 0) || param.AsDouble() > 0.055); //坡度过大或过小时
break;

View File

@@ -25,7 +25,7 @@ public class HeadroomCheckCmd : ExternalCommand
Element element = null;
foreach (var item in elems)
{
if (item.GetLocCurve().Length < minLength)
if (item.GetCurve().Length < minLength)
{
continue;
}

View File

@@ -30,7 +30,6 @@ public partial class InsulationItem : ObservableValidator
private double maxDiameter;
[Range(0, 1000)]
public double MinDiameter
{
get => minDiameter;
@@ -44,12 +43,14 @@ public partial class InsulationItem : ObservableValidator
[Required]
[Range(10, 1000)]
public double Thickness
{
get => thickness;
set => SetProperty(ref thickness, value, true);
}
[NotifyDataErrorInfo]
[ObservableProperty]
private double thickness;
//{
// get => thickness;
// set => SetProperty(ref thickness, value, true);
//}
//private double thickness;
[ObservableProperty]
private PipeInsulationType pipeInsulationType;

View File

@@ -17,37 +17,19 @@ public class MoveConnectCmd : ExternalCommand
Reference moveReference;
try
{
FuncFilter filter =
new(
e =>
((e is MEPCurve mep and not InsulationLiningBase) && mep.GetConnectors(true).Size > 0)
|| (
e is FamilyInstance instance
&& instance.MEPModel.ConnectorManager != null
&& instance.MEPModel.ConnectorManager.UnusedConnectors.Size > 0
)
);
baseReference = UiDocument.Selection.PickObject(
Autodesk.Revit.UI.Selection.ObjectType.Element,
filter,
new FuncFilter(e => e.GetConnectors(true)?.Size > 0),
"请选择基准的管线、管件、设备实例"
);
FuncFilter filter1 =
new(
e =>
e.Id != baseReference.ElementId
&& ((e is MEPCurve mep and not InsulationLiningBase) && mep.GetConnectors(true).Size > 0)
|| (
e is FamilyInstance instance
&& instance.MEPModel.ConnectorManager != null
&& instance.MEPModel.ConnectorManager.UnusedConnectors.Size > 0
&& instance.Id != baseReference.ElementId
)
);
moveReference = UiDocument.Selection.PickObject(
Autodesk.Revit.UI.Selection.ObjectType.Element,
filter,
new FuncFilter(
e =>
e.Id != baseReference.ElementId
&& e.GetConnectors(true)?.Size > 0
),
"请选择需要移动连接的管线、管件、设备实例"
);
}
@@ -120,7 +102,7 @@ public class MoveConnectCmd : ExternalCommand
else
{
//避免交点在原管线外,导致反向连接
if ((mainMEPCurve.GetLocCurve() as Line).IsInsideEx(intersectPoint, 0.5))
if ((mainMEPCurve.GetCurve() as Line).IsInsideEx(intersectPoint, 0.5))
{
ElementTransformUtils.MoveElement(Document, elementToMove!.Id, intersectPoint - origin2);
}

View File

@@ -44,7 +44,7 @@ public partial class MoveMEPCurveViewModel : ObservableValidator
if (!CanRunning)
{
KeyIntPtrHelper.ActionEsc();
KeyIntPtrHelper.RaiseEscTwice();
}
#endregion

View File

@@ -114,7 +114,7 @@ public partial class StandMepCurveViewModel : ObservableObject
return;
}
KeyIntPtrHelper.ActionEsc();
KeyIntPtrHelper.RaiseEscTwice();
}
[RelayCommand(CanExecute = nameof(CanCreate))]

View File

@@ -7,7 +7,7 @@ using System.ComponentModel;
namespace Sai.RvKits.RvMEP
{
public class SystemModel : ObservableObject
public partial class SystemModel : ObservableObject
{
public SystemModel(MEPSystemType systemType)
{
@@ -19,75 +19,28 @@ namespace Sai.RvKits.RvMEP
FillColor = systemType.FillColor;
Abbreviation = systemType.Abbreviation;
}
private string abbreviation;
[ObservableProperty]
private System.Windows.Media.Brush backgroundColor;
[ObservableProperty]
private Color fillColor;
private Color lineColor;
private MEPSystemClassification mepSystemClassification;
[ObservableProperty]
private MEPSystemClassification mEPSystemClassification;
[ObservableProperty]
private string name;
[ObservableProperty]
private PipeSystemType pipeSystemType;
//系统类型
[ObservableProperty]
private MEPSystemType systemType;
public Color LineColor
{
get => lineColor;
set => SetProperty(ref lineColor, value);
}
public string Abbreviation
{
get => abbreviation;
set => SetProperty(ref abbreviation, value);
}
public Color FillColor
{
get => fillColor;
set => SetProperty(ref fillColor, value);
}
public PipeSystemType PipeSystemType
{
get => pipeSystemType;
set => SetProperty(ref pipeSystemType, value);
}
/// <summary>
/// 系统类型
/// </summary>
public MEPSystemType SystemType
{
get => systemType;
set => SetProperty(ref systemType, value);
}
public MEPSystemClassification MEPSystemClassification
{
get => mepSystemClassification;
set => SetProperty(ref mepSystemClassification, value);
}
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
public System.Windows.Media.Brush BackgroundColor
{
get => backgroundColor;
//if (fillColor.IsValid)
//{
// return backgroundColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.SelectedColor.FromRgb(fillColor.Red, fillColor.Green, fillColor.Blue));
//}
//return backgroundColor = new System.Windows.Media.SolidColorBrush();
set => SetProperty(ref backgroundColor, value);
}
[ObservableProperty]
private Color lineColor;
[ObservableProperty]
private string abbreviation;
}
public enum MEPSystemClassification