315 lines
13 KiB
C#
315 lines
13 KiB
C#
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,
|
||
}
|