using System; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Text; using System.Windows; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media.Imaging; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Structure; using Autodesk.Revit.UI; using Autodesk.Revit.UI.Selection; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Win32; using Nice3point.Revit.Toolkit.External.Handlers; using SZMCToolkit.RevitAddins.Assists; using Szmedi.RvKits.Assists; namespace Szmedi.RvKits.Modeling; public partial class InstanceCreatorViewModel : ObservableObject { public InstanceCreatorViewModel(UIApplication uiapp) { uiDocument = uiapp.ActiveUIDocument; document = uiDocument.Document; Families = new FilteredElementCollector(document) .OfClass(typeof(Family)) .Cast() .Where( f => f.IsEditable && !f.IsInPlace && f.GetFamilySymbolIds().Any() && f.FamilyCategory.Id != BuiltInCategory.OST_PipeFitting.GetElementId(document) && f.FamilyCategory.Id != BuiltInCategory.OST_DuctFitting.GetElementId(document) && f.FamilyCategory.Id != BuiltInCategory.OST_CableTrayFitting.GetElementId(document) && f.FamilyCategory.Id != BuiltInCategory.OST_ConduitFitting.GetElementId(document) && f.FamilyCategory.GetHashCode() != -2000127 //过滤掉栏杆族 && f.FamilyPlacementType is FamilyPlacementType.OneLevelBased or FamilyPlacementType.TwoLevelsBased or FamilyPlacementType.WorkPlaneBased ).OrderBy(f => f.Name); FamiliesView = CollectionViewSource.GetDefaultView(Families); } [RelayCommand] private void PreviewTextInput(TextCompositionEventArgs args) { if (args != null && args.Source is System.Windows.Controls.ComboBox cb) { if (cb.Text == cb.SelectedItem?.ToString()) { args.Handled = true; } } } private readonly ActionEventHandler handler = new(); private readonly Document document; private readonly UIDocument uiDocument; [ObservableProperty] private BitmapSource image; [ObservableProperty] public partial string SearchText { get; set; } partial void OnSearchTextChanged(string value) { if (Families != null) { FamiliesView.Filter = item => { if (item is Family family) { return family.Name.Contains(value); } return false; }; FamiliesView.Refresh(); } } [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CloseWinCommand))] private bool canPlace; [ObservableProperty] private double offset; [ObservableProperty] private Family selectedFamily; [ObservableProperty] private FamilySymbol selectedFamilySymbol; [ObservableProperty] private IEnumerable families; public ICollectionView FamiliesView { get; set; } [ObservableProperty] private IEnumerable familySymbols; [ObservableProperty] private List relatedProps; [RelayCommand(CanExecute = nameof(CanPlace))] private void CloseWin() { var d = Offset / 304.8; try { var reference = uiDocument.Selection.PickObject( ObjectType.PointOnElement, new DwgBlockSelection(), "请选择dwg块" ); //图层ID; //doc.HideDwgLayer(reference, out ElementId columnGraphicStyleId, out GeometryElement columnGeoElem); var dwg = document.GetElement(reference) as ImportInstance; var geoInstances = dwg.GetBlockInstance(reference); if (geoInstances == null || !geoInstances.Any()) { MessageBox.Show("选中块为嵌套子块或选中块的嵌套层级过深,请使用Tab来选择整体块", "识别错误", MessageBoxButton.OK, MessageBoxImage.Error); CanPlace = true; return; } var dwgTransform = dwg!.GetTotalTransform(); handler.Raise(_ => { var sb = new StringBuilder(); var instances = new List(); document.InvokeGroup(_ => { document.Invoke(_ => { if (!SelectedFamilySymbol.IsActive) { SelectedFamilySymbol.Activate(); } switch (SelectedFamily.FamilyPlacementType) { case FamilyPlacementType.OneLevelBased: foreach (var ins in geoInstances) { var level = uiDocument.ActiveView.GenLevel; GetLocating(dwgTransform, ins, out var loc, out var rotation); var familyInstance = document.Create.NewFamilyInstance(loc, SelectedFamilySymbol, level, StructuralType.NonStructural); document.Regenerate(); ElementTransformUtils.RotateElement( document, familyInstance.Id, Line.CreateUnbound(loc, XYZ.BasisZ), rotation ); document.Regenerate(); var translation = XYZ.BasisZ * d; ElementTransformUtils.MoveElement(document, familyInstance.Id, translation); instances.Add(familyInstance); } break; case FamilyPlacementType.OneLevelBasedHosted: //foreach (var ins in geoInstances) //{ // var level = uiDocument.ActiveView.GenLevel; // GetLocating(dwgTransform, ins, out var loc, out var rotation); // var familyInstance = // doc.Create.NewFamilyInstance(loc, SelectedFamilySymbol, level, level, StructuralType.NonStructural); // doc.Regenerate(); // ElementTransformUtils.RotateElement( // doc, // familyInstance.Id, // Line.CreateUnbound(loc, XYZ.BasisZ), // rotation // ); // doc.Regenerate(); // var translation = XYZ.BasisZ * d; // ElementTransformUtils.MoveElement(doc, familyInstance.Id, translation); // instances.Add(familyInstance); //} break; case FamilyPlacementType.TwoLevelsBased: foreach (var ins in geoInstances) { var level = uiDocument.ActiveView.GenLevel; GetLocating(dwgTransform, ins, out var loc, out var rotation); var familyInstance = document.Create.NewFamilyInstance(loc, SelectedFamilySymbol, level, StructuralType.NonStructural); document.Regenerate(); ElementTransformUtils.RotateElement( document, familyInstance.Id, Line.CreateUnbound(loc, XYZ.BasisZ), rotation ); document.Regenerate(); var translation = XYZ.BasisZ * d; ElementTransformUtils.MoveElement(document, familyInstance.Id, translation); instances.Add(familyInstance); } break; case FamilyPlacementType.WorkPlaneBased: foreach (var ins in geoInstances) { GetLocating(dwgTransform, ins, out var loc, out var rotation); var familyInstance = document.Create.NewFamilyInstance( loc, SelectedFamilySymbol, document.ActiveView.SketchPlane, StructuralType.NonStructural ); document.Regenerate(); ElementTransformUtils.RotateElement( document, familyInstance.Id, Line.CreateUnbound(loc, XYZ.BasisZ), rotation ); document.Regenerate(); var translation = XYZ.BasisZ * d; ElementTransformUtils.MoveElement(document, familyInstance.Id, translation); instances.Add(familyInstance); } break; } }); var propsModify = RelatedProps.Where(p => p.IsChecked && !string.IsNullOrEmpty(p.LayerName)).ToList(); if (propsModify.Any()) { document.Invoke(_ => { foreach (var instance in instances) { var box = instance.get_BoundingBox(document.ActiveView); var center = (box.Max + box.Min) / 2; var loc = center.Flatten(); foreach (var prop in propsModify) { var param = instance.GetParameters(prop.Name).FirstOrDefault(); if (param == null) { sb.AppendLine($"族实例:{instance.Name}不存在参数\"{prop.Name}\""); continue; } if (param.IsReadOnly) { sb.AppendLine($"族实例:{instance.Name}的\"{prop.Name}\"为只读参数:"); continue; } var texts = dwg.GetTextsByLayer(prop.LayerName); var keyValue = texts.OrderBy(text => text.Value.Flatten().DistanceTo(loc)).First(); if (keyValue.Value.Flatten().DistanceTo(loc) > 10) { sb.AppendLine($"标注文字与族实例\"{instance.Name}-{instance.Id}\"距离过远,可能有误"); } param.SetValue(keyValue.Key); //param.Set(keyValue.Key); } } }); } if (sb.Length > 0) { MessageBox.Show(sb.ToString(), "警告", MessageBoxButton.OK, MessageBoxImage.Warning); } var result = MessageBox.Show("布置完成,是否导出布置结果以供检查?", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Information); if (result == MessageBoxResult.OK) { SaveFileDialog dialog = new() { Filter = WinDialogAssists.CreateFilter("Csv文件", "csv"), InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop), }; if (dialog.ShowDialog() != true) { return; } var excel = dialog.FileName; //FileInfo fi = new(excel); //using ExcelPackage package = new(fi); //ExcelPackage excelPackage = Instances.ToWorksheet("< 1950") // .WithConfiguration(configuration => configuration.WithColumnConfiguration(x => x.AutoFit())) // .WithColumn(x => x.FirstName, "First Name") // .WithColumn(x => x.LastName, "Last Name") // .WithColumn(x => x.YearBorn, "Year of Birth") // .WithTitle("< 1950") // .NextWorksheet(post50, "> 1950") // .WithColumn(x => x.LastName, "Last Name") // .WithColumn(x => x.YearBorn, "Year of Birth") // .WithTitle("> 1950") // .ToExcelPackage(); using (StreamWriter writer = new(excel, false, Encoding.UTF8)) { writer.Write("族名称,族实例,元素Id"); foreach (var prop in RelatedProps) { writer.Write($",{prop.Name}"); } writer.WriteLine(); foreach (var instance in instances) { writer.Write($"{instance.Symbol.FamilyName},{instance.Name},{instance.Id.IntegerValue}"); foreach (var prop in RelatedProps) { var param = instance.GetParameters(prop.Name).FirstOrDefault(); if (param != null) { var value = param.GetValue(); writer.Write($",{value}"); } else { writer.Write(","); } } writer.WriteLine(); //换行 } } Process.Start(excel); } }, "族实例布置"); }); } catch (Autodesk.Revit.Exceptions.OperationCanceledException) { } catch (Exception ex) { LogAssists.WriteLog(ex.Message); } } /// /// 设置元素几何变换 /// /// /// /// /// private static void GetLocating(Transform dwgTransform, GeometryInstance ins, out XYZ loc, out double rotation) { var blockLocation = ins.Transform.Origin; if (ins.Transform.Origin.GetLength() is > 1000 or 0) { foreach (var geometryObject in ins.GetInstanceGeometry()) { if (geometryObject is Arc arc) { blockLocation = arc.Center; break; } if (geometryObject is PolyLine pl) { blockLocation = (pl.GetOutline().MaximumPoint + pl.GetOutline().MinimumPoint) / 2; break; } } } loc = dwgTransform.OfPoint(blockLocation); loc = new XYZ(loc.X, loc.Y, 0); rotation = Math.Abs(ins.Transform.BasisX.AngleOnPlaneTo(XYZ.BasisX, XYZ.BasisZ) - 2 * Math.PI); } //[RelayCommand] //private void AutoComplete(string searchText) //{ // SelectedFamily = Families.FirstOrDefault(f => f.Name.Contains(searchText))!; //} partial void OnSelectedFamilyChanged(Family value) { if (value != null) { FamilySymbols = value.GetFamilySymbolIds().Select(id => value.Document.GetElement(id)).OfType(); } } partial void OnSelectedFamilySymbolChanged(FamilySymbol value) { var map = FamilySymbols.FirstOrDefault()?.GetPreviewImage(new System.Drawing.Size(128, 128)); if (map != null) { Image = map.ToBitmapSource(); } CanPlace = true; RelatedProps = new List(); //var list = new List(); //foreach (Parameter parameter in value.ParametersMap) //{ // list.Add(new RelatedProp() // { // Id = parameter.Id.IntegerValue, // Name = parameter.Definition.Name, // PropertyType = "类型", // }); //} //foreach (Parameter parameter in value.Parameters) //{ // var found = list.Any(p => p.Id == parameter.Id.IntegerValue); // if (!found) // { // list.Add(new RelatedProp() // { // Id = parameter.Id.IntegerValue, // Name = parameter.Definition.Name, // PropertyType = "实例", // }); // } //} //RelatedProps = list; } [RelayCommand] private void PickLayer(object prop) { try { var textRefer = uiDocument.Selection.PickObject( ObjectType.PointOnElement, new FuncFilter(e => e is ImportInstance import && import.IsLinked && document.GetElement(e.GetTypeId()) is CADLinkType), "请选择“链接(非导入)”的Cad文字图层"); var dwg = document.GetElement(textRefer) as ImportInstance; var textLayerName = dwg.GetLayerName(textRefer); if (prop is RelatedProp relatedProp) { relatedProp.LayerName = textLayerName; } } catch (Autodesk.Revit.Exceptions.OperationCanceledException) { } } }