using System.Collections.ObjectModel; using System.Windows; using System.Windows.Data; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Nice3point.Revit.Toolkit.External.Handlers; namespace Sai.RvKits.RvView; public partial class SectionBoxControllerViewModel : ObservableObject { public SectionBoxControllerViewModel(UIDocument uidoc) { uidoc.Application.ViewActivated -= Application_ViewActivated; uidoc.Application.ViewActivated += Application_ViewActivated; if (uidoc.ActiveView is View3D view3d) { using var trans = new Transaction(uidoc.Document, "激活剖面框"); trans.Start(); if (!view3d.IsSectionBoxActive) { view3d.IsSectionBoxActive = true; } //defaultSectionBox = view3D.GetSectionBox(); trans.Commit(); CanModify = true; } else { CanModify = false; } setSectionBoxHandler = new ActionEventHandler(); this.uidoc = uidoc; } private void Application_ViewActivated(object sender, Autodesk.Revit.UI.Events.ViewActivatedEventArgs e) { CanModify = e.CurrentActiveView is View3D view3d && view3d.IsSectionBoxActive; } [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(RecordSectionBoxCommand), nameof(UpdateSectionBoxCommand))] private bool canModify; [ObservableProperty] private SectionBoxType sectionBoxType; [ObservableProperty] private bool needExtend; /// /// 记录剖面框修改 /// public ObservableCollection BoundingBoxes { get; set; } = []; private readonly ActionEventHandler setSectionBoxHandler; private readonly UIDocument uidoc; private int index = 1; [RelayCommand(CanExecute = nameof(CanModify))] private void RecordSectionBox() { if (uidoc.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(_ => { var doc = uidoc.Document; switch (SectionBoxType) { case SectionBoxType.Element: ResetSectionBoxByElement(); break; case SectionBoxType.Rectangle: ResetSectionBoxByRectangle(); break; case SectionBoxType.ViewSection: ResetSectionBoxByViewSection(); break; default: break; } }); } /// /// 重设元素剖面框 /// private void ResetSectionBoxByElement() { var doc = uidoc.Document; doc.Invoke( ts => { if (doc.ActiveView is not View3D view3D) { var viewFamilyType = doc.OfClass() .Cast() .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; } 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); } if (!boundingBox.Enabled) { MessageBox.Show("当前三维剖面框不可用", "错误"); } var newbox = new BoundingBoxXYZ { Max = new XYZ(maxX, maxY, maxZ), Min = new XYZ(minX, minY, minZ) }; //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; }, "重设(元素)剖面框"); } /// /// 重设框选剖面框 /// private void ResetSectionBoxByRectangle() { 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() .Cast() .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() .Cast() .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; }, "重设(框选)剖面框"); } /// /// 重设剖面视图范围的剖面框 /// private void ResetSectionBoxByViewSection() { 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() .Cast() .FirstOrDefault(vft => vft.ViewFamily == ViewFamily.ThreeDimensional); //新建的三维视图 var view3D = View3D.CreateIsometric(doc, viewFamilyType.Id); if (doc.ActiveView is not ViewSection viewSection) { viewSection = doc.OfClass().Cast().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(CanModify))] private void UpdateSectionBox(object obj) { if (obj is BoundingBoxXYZ boundingBoxXyz) { setSectionBoxHandler.Raise(_ => { 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, }