Files
SzmediTools/Szmedi.RvKits/Modeling/InstanceCreatorViewModel.cs

461 lines
19 KiB
C#
Raw Normal View History

2025-09-16 16:06:41 +08:00

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<Family>()
.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]
private string searchText;
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<Family> families;
public ICollectionView FamiliesView { get; set; }
[ObservableProperty]
private IEnumerable<FamilySymbol> familySymbols;
[ObservableProperty]
private List<RelatedProp> 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<FamilyInstance>();
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);
}
}
/// <summary>
/// 设置元素几何变换
/// </summary>
/// <param name="dwgTransform"></param>
/// <param name="ins"></param>
/// <param name="loc"></param>
/// <param name="rotation"></param>
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<FamilySymbol>();
}
}
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<RelatedProp>();
//var list = new List<RelatedProp>();
//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)
{
}
}
}