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; }
///
/// 记录剖面框修改
///
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,
}