Files
Shrlalgo.RvKits/ShrlAlgoToolkit.RevitAddins/DrawSheet/SectionBoxControllerViewModel.cs
2026-02-22 20:03:42 +08:00

315 lines
13 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.Collections.ObjectModel;
using System.Windows;
using Autodesk.Revit.DB;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Nice3point.Revit.Toolkit.External.Handlers;
namespace ShrlAlgoToolkit.RevitAddins.RvView;
public partial class SectionBoxControllerViewModel : ObservableObject
{
public SectionBoxControllerViewModel(UIApplication uiapp)
{
uiapp.ViewActivated -= Application_ViewActivated;
uiapp.ViewActivated += Application_ViewActivated;
if (uiapp.ActiveUIDocument.ActiveView is View3D view3d)
{
using var trans = new Transaction(uiapp.ActiveUIDocument.Document, "激活剖面框");
trans.Start();
if (!view3d.IsSectionBoxActive)
{
view3d.IsSectionBoxActive = true;
}
//defaultSectionBox = view3D.GetSectionBox();
trans.Commit();
CanModify = true;
}
else
{
CanModify = false;
}
setSectionBoxHandler = new ActionEventHandler();
this.uiapp = uiapp;
}
private void Application_ViewActivated(object sender, Autodesk.Revit.UI.Events.ViewActivatedEventArgs e)
{
CanModify = e.CurrentActiveView is View3D view3d && view3d.IsSectionBoxActive;
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(RevitAddins.RvView.SectionBoxControllerViewModel.RecordSectionBoxCommand), nameof(RevitAddins.RvView.SectionBoxControllerViewModel.UpdateSectionBoxCommand))]
public partial bool CanModify { get; set; }
[ObservableProperty]
public partial SectionBoxType SectionBoxType { get; set; }
[ObservableProperty]
public partial bool NeedExtend { get; set; }
/// <summary>
/// 记录剖面框修改
/// </summary>
public ObservableCollection<SectionBox> BoundingBoxes { get; set; } = [];
private readonly ActionEventHandler setSectionBoxHandler;
private readonly UIApplication uiapp;
private int index = 1;
[RelayCommand(CanExecute = nameof(RevitAddins.RvView.SectionBoxControllerViewModel.CanModify))]
private void RecordSectionBox()
{
if (uiapp.ActiveUIDocument.Document.ActiveView is View3D { IsSectionBoxActive: true } view3D)
{
BoundingBoxes.Insert(0, new SectionBox($"剖面框 {index}", view3D.GetSectionBox()));
index++;
}
}
[RelayCommand]
private void RemoveSectionBox(SectionBox sectionBox)
{
BoundingBoxes.Remove(sectionBox);
}
[RelayCommand]
private void ResetSectionBox()
{
setSectionBoxHandler.Raise(uiapp =>
{
var uidoc = uiapp.ActiveUIDocument;
switch (SectionBoxType)
{
case SectionBoxType.Element:
ResetSectionBoxByElement(uidoc);
break;
case SectionBoxType.Rectangle:
ResetSectionBoxByRectangle(uidoc);
break;
case SectionBoxType.ViewSection:
ResetSectionBoxByViewSection(uidoc);
break;
default:
break;
}
});
}
/// <summary>
/// 重设元素剖面框
/// </summary>
private void ResetSectionBoxByElement(UIDocument uidoc)
{
var doc = uidoc.Document;
doc.Invoke(
ts =>
{
if (doc.ActiveView is not View3D view3D)
{
var viewFamilyType = doc.OfClass<ViewFamilyType>()
.Cast<ViewFamilyType>()
.FirstOrDefault(vft => vft.ViewFamily == ViewFamily.ThreeDimensional);
view3D = View3D.CreateIsometric(doc, viewFamilyType.Id);
view3D.DisplayStyle = DisplayStyle.ShadingWithEdges;
view3D.DetailLevel = ViewDetailLevel.Fine;
}
var elements = uidoc.GetSelectedElements();
if (elements.Count == 0)
{
return;
}
XYZ gmax = XYZ.Zero;
XYZ gmin = XYZ.Zero;
doc.InvokeSub(
sub =>
{
//var boundingBox = elements[0].get_BoundingBox(view3D);
//var maxX = boundingBox.Max.X;
//var maxY = boundingBox.Max.Y;
//var maxZ = boundingBox.Max.Z;
//var minX = boundingBox.Min.X;
//var minY = boundingBox.Min.Y;
//var minZ = boundingBox.Min.Z;
//foreach (var elem in elements)
//{
// var box = elem.get_BoundingBox(view3D);
// maxX = Math.Max(box.Max.X, maxX);
// maxY = Math.Max(box.Max.Y, maxY);
// maxZ = Math.Max(box.Max.Z, maxZ);
// minX = Math.Min(box.Min.X, minX);
// minY = Math.Min(box.Min.Y, minY);
// minZ = Math.Min(box.Min.Z, minZ);
//}
var elementIds = elements.Select(e => e.Id).ToList();
var g = doc.Create.NewGroup(elementIds);
var boundingBox = g.get_BoundingBox(null);
gmax = g.get_BoundingBox(null).Max;
gmin = g.get_BoundingBox(null).Min;
sub.RollBack();
if (!boundingBox.Enabled)
{
MessageBox.Show("当前三维剖面框不可用", "错误");
}
});
//var newbox = new BoundingBoxXYZ
//{
// Max = new XYZ(maxX, maxY, maxZ),
// Min = new XYZ(minX, minY, minZ)
//};
var newbox = new BoundingBoxXYZ
{
Max = gmax,
Min = gmin
};
//XYZ origin = new XYZ(0, 0, 0);
//XYZ axis = new XYZ(0, 0, 1);
//Transform rotate = Transform.CreateRotationAtPoint(axis, 2, origin);
//box.Transform = box.Transform.Multiply(rotate);
newbox = NeedExtend ? newbox.Extend() : newbox;
view3D?.SetSectionBox(newbox);
ts.Commit();
uidoc.ActiveView = view3D;
},
"重设(元素)剖面框");
}
/// <summary>
/// 重设框选剖面框
/// </summary>
private void ResetSectionBoxByRectangle(UIDocument uidoc)
{
var doc = uidoc.Document;
doc.Invoke(
ts =>
{
if (uidoc.ActiveView is not ViewPlan viewPlane)
{
MessageBox.Show("请在平面上框选范围", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var box = uidoc.Selection.PickBox(Autodesk.Revit.UI.Selection.PickBoxStyle.Directional);
var max = box.Max;
var min = box.Min;
var currentLevel = viewPlane.GenLevel;
var level = doc.OfClass<Level>()
.Cast<Level>()
.OrderBy(l => l.Elevation)
.FirstOrDefault(l => l.Elevation > currentLevel.Elevation);
var boundMax = new XYZ(Math.Max(max.X, min.X), Math.Max(max.Y, min.Y), level.Elevation);
var boundMin = new XYZ(Math.Min(max.X, min.X), Math.Min(max.Y, min.Y), min.Z);
var bounding = new BoundingBoxXYZ() { Max = boundMax, Min = boundMin };
var viewFamilyType = doc.OfClass<ViewFamilyType>()
.Cast<ViewFamilyType>()
.FirstOrDefault(vft => vft.ViewFamily == ViewFamily.ThreeDimensional);
var view3D = View3D.CreateIsometric(doc, viewFamilyType.Id);
view3D.IsSectionBoxActive = true;
bounding = NeedExtend ? bounding : bounding.Extend();
view3D.SetSectionBox(bounding);
view3D.DisplayStyle = DisplayStyle.ShadingWithEdges;
view3D.DetailLevel = ViewDetailLevel.Fine;
ts.Commit();
uidoc.ActiveView = view3D;
},
"重设(框选)剖面框");
}
/// <summary>
/// 重设剖面视图范围的剖面框
/// </summary>
private void ResetSectionBoxByViewSection(UIDocument uidoc)
{
var reference = uidoc.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element, new FuncFilter(elem => elem.Category?.GetHashCode() == -2000278), "请选择剖面视图");
var doc = uidoc.Document;
var element = doc.GetElement(reference);
if (element != null)
{
doc.Invoke(
ts =>
{
var viewFamilyType = doc.OfClass<ViewFamilyType>()
.Cast<ViewFamilyType>()
.FirstOrDefault(vft => vft.ViewFamily == ViewFamily.ThreeDimensional);
//新建的三维视图
var view3D = View3D.CreateIsometric(doc, viewFamilyType.Id);
if (doc.ActiveView is not ViewSection viewSection)
{
viewSection = doc.OfClass<View>().Cast<View>().FirstOrDefault(v => v.Name == element.Name && v.ViewType == ViewType.Section && !v.IsTemplate) as ViewSection;
}
view3D.IsSectionBoxActive = true;
doc.Regenerate();
//最新添加的即是当前复制视图的剖面框
//var cropBoxElement = view3D.OfCategory(BuiltInCategory.OST_SectionBox).Last();
//实际位置的BoundingBox
//var bounding = element.get_BoundingBox(viewSection);
//相对剖面的BoundingBox,准确的剖面框大小,具有transform
var boundingBox = viewSection.CropBox;
//var b = Transform.CreateRotationAtPoint(viewSection.RightDirection, Math.PI / 2, viewSection.Origin);
//重新构造一个变换由于视图的变换r x v = u满足左手定则与全局坐标系右手定则不一样将其坐标轴修改为全局坐标系的一样的右手定则
var transform = Transform.Identity;
transform.BasisX = viewSection.RightDirection;
//Y轴取反
transform.BasisY = -viewSection.ViewDirection;
transform.BasisZ = viewSection.UpDirection;
transform.Origin = boundingBox.Transform.Origin;
//boundingBox.Max.Z固定等于0因为ViewDirection朝外面向用户Min的Z值始终小于0即反向
var max = new XYZ(boundingBox.Max.X, -boundingBox.Min.Z, boundingBox.Max.Y);
var min = new XYZ(boundingBox.Min.X, -boundingBox.Max.Z, boundingBox.Min.Y);
boundingBox.Max = max;
boundingBox.Min = min;
boundingBox.Transform = transform;
boundingBox = NeedExtend ? boundingBox : boundingBox.Extend();
view3D.SetSectionBox(boundingBox);
//ElementTransformUtils.MoveElement(doc, cropBoxElement.Id, viewSection.Origin);
view3D.DisplayStyle = DisplayStyle.ShadingWithEdges;
view3D.DetailLevel = ViewDetailLevel.Fine;
ts.Commit();
uidoc.ActiveView = view3D;
},
"重设(剖面)剖面框");
}
}
[RelayCommand(CanExecute = nameof(RevitAddins.RvView.SectionBoxControllerViewModel.CanModify))]
private void UpdateSectionBox(object obj)
{
if (obj is BoundingBoxXYZ boundingBoxXyz)
{
setSectionBoxHandler.Raise(uiapp =>
{
var uidoc = uiapp.ActiveUIDocument;
if (uidoc.ActiveView is View3D view3d)
{
uidoc.Document.Invoke(_ =>
{
view3d?.SetSectionBox(boundingBoxXyz);
}, "更新剖面框");
}
});
}
}
}
public class SectionBox(string name, BoundingBoxXYZ bounding)
{
public string Name { get; set; } = name;
public BoundingBoxXYZ Bounding { get; set; } = bounding;
}
public enum SectionBoxType
{
Element,
Rectangle,
ViewSection,
}