using System.Collections.ObjectModel; using System.Windows; using Autodesk.Revit.DB; using Autodesk.Revit.UI; 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))] private bool canModify; [ObservableProperty] private SectionBoxType sectionBoxType; [ObservableProperty] private bool needExtend; /// /// 记录剖面框修改 /// public ObservableCollection 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; } }); } /// /// 重设元素剖面框 /// private void ResetSectionBoxByElement(UIDocument uidoc) { 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; } 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; }, "重设(元素)剖面框"); } /// /// 重设框选剖面框 /// 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() .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(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() .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(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, }