Files
SzmediTools/Szmedi.RvKits/Modeling/InstanceCreatorViewModel.cs
2025-09-19 09:18:09 +08:00

462 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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]
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<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)
{
}
}
}