using System.Linq; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Electrical; using Autodesk.Revit.DB.Mechanical; using Autodesk.Revit.DB.Plumbing; using Autodesk.Revit.UI; using Nice3point.Revit.Toolkit.External.Handlers; namespace Szmedi.RvKits.MEPTools { public partial class MEPCurveSignViewModel : ObservableValidator { private readonly ActionEventHandler handler; public MEPCurveSignViewModel() { handler = new ActionEventHandler(); } [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(BatchSignsCommand))] private string newSign = "生活给水管道"; [ObservableProperty] [IsNumeric] [NotifyDataErrorInfo] [Minimum(0)] [NotifyCanExecuteChangedFor(nameof(CreateSignsCommand))] private double minSize = 50; [ObservableProperty] [IsNumeric] [NotifyDataErrorInfo] [Minimum(0)] [NotifyCanExecuteChangedFor(nameof(CreateSignsCommand))] private double minLength = 600; [ObservableProperty] private bool useArrow; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CreateSignsCommand))] private bool containPipes; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CreateSignsCommand))] private bool containDucts; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CreateSignsCommand))] private bool containCableTrays; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CreateSignsCommand))] private bool containConduits; private bool CanCreateSigns => MinSize >= 0 && MinLength >= 0 && (ContainPipes || ContainDucts || ContainCableTrays || ContainConduits); [RelayCommand(CanExecute = nameof(CanCreateSigns))] private void CreateSigns() { handler.Raise( uiapp => { Document doc = uiapp.ActiveUIDocument.Document; doc.Invoke( _ => { var family = doc.OfClass() .Cast() .FirstOrDefault(f => f.Name == "机电管线标识"); if (family == null) { var path = Path.Combine(GlobalVariables.DirAssembly, "FamilyLib", "2020", "机电管线标识.rfa"); doc.LoadFamily(path, out family); } var symbol = doc.GetElement(family.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol; if (!symbol.IsActive) { symbol.Activate(); } var mepCurves = doc.ActiveView .OfCollector() .OfClass(typeof(MEPCurve)) .Cast() .Where( c => (c is Duct && ContainDucts) || (c is CableTray && ContainCableTrays) || (c is Pipe && ContainPipes) || (c is Conduit && ContainConduits)) .ToList(); foreach (var mepCurve in mepCurves) { var conn = mepCurve.ConnectorManager.Connectors.OfType().FirstOrDefault(); var shape = conn.Shape; var locLine = mepCurve.GetLocCurve() as Line; if (locLine.Length - MinLength / 304.8 < 10e-6) { continue; } switch (shape) { case ConnectorProfileType.Round: if (mepCurve.Diameter - MinSize / 304.8 < 10e-6) { continue; } break; default: if (mepCurve.Width - MinSize / 304.8 < 10e-6) { continue; } break; } var centerPoint = locLine.Evaluate(0.5, true); Line l = Line.CreateUnbound(centerPoint, XYZ.BasisZ); if (locLine.Direction.CrossProduct(XYZ.BasisZ).IsAlmostEqualTo(XYZ.Zero))//立管 { l = Line.CreateUnbound(centerPoint, XYZ.BasisX); } XYZ loc = null; XYZ referenceVector = XYZ.Zero; var options = new Options() { ComputeReferences = true, }; var geoElem = mepCurve.get_Geometry(options); Face face = null; foreach (var obj in geoElem) { if (obj is Solid solid) { var faces = solid.Faces; if (faces == null || faces.Size == 0) { continue; } foreach (Face cface in faces) { var result = cface.Intersect(l, out var array); if (result == SetComparisonResult.Overlap) { face = cface; loc = array.get_Item(0).XYZPoint; var uv = array.get_Item(0).UVPoint; var faceNormal = face.ComputeNormal(uv); if (faceNormal.IsAlmostEqualTo(XYZ.BasisZ)) { break; } } } if (face != null && loc != null) { break; } } } if (loc == null) { continue; } var instance = doc.Create .NewFamilyInstance( face, loc, referenceVector, symbol); doc.Regenerate(); string systemName = string.Empty; switch (conn.Domain) { case Domain.DomainHvac: systemName = mepCurve.get_Parameter(BuiltInParameter.RBS_DUCT_SYSTEM_TYPE_PARAM).AsValueString(); break; case Domain.DomainPiping: systemName = mepCurve.get_Parameter(BuiltInParameter.RBS_PIPING_SYSTEM_TYPE_PARAM).AsValueString(); break; case Domain.DomainCableTrayConduit: systemName = mepCurve.get_Parameter(BuiltInParameter.RBS_CTC_SERVICE_TYPE).AsValueString(); break; default: break; } if (string.IsNullOrEmpty(systemName)) { systemName = "未定义管线"; } var split = systemName.Split('_', '-'); if (split.Length > 1)//具有多字段 { systemName = split.LastOrDefault(); } systemName = systemName.Replace("系统", "管道"); instance.LookupParameter("机电管线标识").Set(systemName); instance.LookupParameter("显示箭头").Set(UseArrow ? 1 : 0); var refer = instance.HandOrientation; var normal = refer.CrossProduct(locLine.Direction); if (normal.IsAlmostEqualTo(XYZ.Zero)) { continue; } var angle = refer.AngleTo(locLine.Direction); ElementTransformUtils.RotateElement( doc, instance.Id, Line.CreateUnbound(loc, normal), angle); } }, "创建管线标识"); }); } private bool CanBatchSigns => !string.IsNullOrEmpty(NewSign); [RelayCommand(CanExecute = nameof(CanBatchSigns))] private void BatchSigns() { handler.Raise( uiapp => { if (string.IsNullOrEmpty(NewSign)) { return; } var uidoc = uiapp.ActiveUIDocument; var doc = uidoc.Document; var ids = uidoc.Selection.GetElementIds(); var signs = doc.ActiveView.OfCollector().OfClass(typeof(FamilyInstance)) .Cast() .Where(e => e.Name == "机电管线标识") .ToList(); if (ids.Count == 0) { TaskDialog.Show("提示", "请选择机电管线"); return; } if (signs.Count == 0) { TaskDialog.Show("提示", "当前视图中没有机电管线标识的实例"); return; } doc.Invoke( _ => { foreach (var sign in signs) { if (sign.Host is MEPCurve host && ids.Contains(host.Id)) { sign.GetParameters("机电管线标识").FirstOrDefault()?.Set(NewSign); } } }, "批处理标识"); }); } } }